【问题标题】:Websocket timeout with Apache Wicket and embedded JettyApache Wicket 和嵌入式 Jetty 的 Websocket 超时
【发布时间】:2014-01-06 19:23:37
【问题描述】:

我正在使用带有嵌入式 Jetty 网络服务器的 Wicket。该应用程序正在使用 websockets,因此 Jetty 设置如下所示:

FilterHolder filterHolder = new FilterHolder(Jetty9WebSocketFilter.class);
filterHolder.setInitParameter("applicationClassName", "MyApplication");
filterHolder.setInitParameter("filterMappingUrlPattern", "/*");

WebAppContext context = new WebAppContext();
context.setResourceBase(".");
context.addFilter(filterHolder, "/*", null);
context.addServlet(DefaultServlet.class, "/*");

Server server = new Server(8083);
server.setHandler(context);

try {
    server.start();
} catch (Exception e) {
    e.printStackTrace();
}

一切正常,除了 websocket 超时。研究表明,Jetty 的 websocket 连接超时,尽管这对于 web 服务器来说并不常见。

经过研究,我偶然发现了以下初始化参数,现在我将其传递给我的filterHolder

filterHolder.setInitParameter("maxIdleTime", "5000");

显然这个参数做了一些事情 - 因为现在超时发生的速度比以前快得多,正好在 5 秒之后。

但我不知道如何完全禁用超时。使用-10 甚至Integer.MIN_VALUE 而不是5000 什么都不做,一段时间后仍然有超时。我找到的文档没有说明相应的值。

我可以使用什么初始化参数来禁用 websocket 超时?还是我必须坚持将超时设置为某个高得离谱的值?

【问题讨论】:

    标签: websocket wicket embedded-jetty


    【解决方案1】:

    Jetty 9.1 有自己的WebSocketUpgradeFilter,使用那个,然后修改默认策略的空闲超时。

    例子:

    package jetty.websocket;
    
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.ServerConnector;
    import org.eclipse.jetty.servlet.DefaultServlet;
    import org.eclipse.jetty.servlet.ServletContextHandler;
    import org.eclipse.jetty.servlet.ServletHolder;
    import org.eclipse.jetty.websocket.api.Session;
    import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
    import org.eclipse.jetty.websocket.api.annotations.WebSocket;
    import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
    import org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec;
    import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
    import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
    import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
    
    public class JettyWebSocketViaFilter
    {
        @WebSocket
        public static class EchoSocket
        {
            @OnWebSocketMessage
            public void onMessage(Session session, String msg)
            {
                session.getRemote().sendStringByFuture(msg);
            }
        }
    
        public static class EchoCreator implements WebSocketCreator
        {
            private EchoSocket echoer = new EchoSocket();
    
            @Override
            public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
            {
                return echoer;
            }
        }
    
        public static void main(String[] args)
        {
            Server server = new Server();
            ServerConnector connector = new ServerConnector(server);
            connector.setPort(8080);
            server.addConnector(connector);
    
            // Setup the basic application "context" for this application at "/"
            // This is also known as the handler tree (in jetty speak)
            ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
            context.setContextPath("/");
            server.setHandler(context);
    
            // Add the websocket filter
            WebSocketUpgradeFilter wsfilter = WebSocketUpgradeFilter.configureContext(context);
            wsfilter.getFactory().getPolicy().setIdleTimeout(5000);
            wsfilter.addMapping(new ServletPathSpec("/"),new EchoCreator());
    
            // The filesystem paths we will map
            String staticRoot = "src/main/webapp/websocket/protocol";
    
            ServletHolder holderDefault = new ServletHolder("default",DefaultServlet.class);
            holderDefault.setInitParameter("resourceBase",staticRoot);
            holderDefault.setInitParameter("dirAllowed","true");
            context.addServlet(holderDefault,"/");
    
            try
            {
                server.start();
                server.join();
            }
            catch (Throwable t)
            {
                t.printStackTrace(System.err);
            }
        }
    }
    

    使用 WebSocket 的 Jetty 超时:

    由于 WebSocket 是升级后的 HTTP/1.1 请求,您基本上需要担心 2 个超时。

    首先是连接器空闲超时,它将用于传入升级请求的 HTTP/1.1 初始部分。这是在服务器级别配置的,具有它所具有的连接器。使用 Jetty,任何 0 或以下的值都被视为无限超时。

    接下来,您将获得特定于 websocket 端点的空闲超时。如果它的值大于 0,则应用于已建立连接的空闲超时(来自服务器端的超时)。

    一些组合及其含义...

    Server Connector IdleTimeout  | WebSocket Endpoint IdleTimeout | Actual Timeout
    ------------------------------+--------------------------------+----------------
       30,000 ms                  |       -1                       |   30,000 ms
       30,000 ms                  |   10,000 ms                    |   10,000 ms
       30,000 ms                  |  400,000 ms                    |  400,000 ms
      500,000 ms                  |       -1                       |  500,000 ms
      500,000 ms                  |      200 ms                    |      200 ms
           -1                     |       -1                       |  (infinite)
           -1                     |    1,000 ms                    |    1,000 ms
    

    您可以将服务器连接器 IdleTimeout 视为 TCP 级别的超时,而 WebSocket 端点超时是应用程序级别的空闲超时。

    希望这会有所帮助。

    【讨论】:

    • 继续让 Wicket 知道这个过滤器...issues.apache.org/jira/browse/WICKET-5453
    • 我使用了您的代码,包括 EchoSocketEchoCreator 类,但我的检票口应用程序现在不知道打开的 Web 套接字连接。我当前的检票口页面上有一个WebSocketBehavior,以前在打开 websocket 连接时收到通知。现在调用了EchoCreatorcreateWebSocket 方法,但检票口不再得到通知。
    • 另外说明,我最初的问题仍未得到解答。 WebSocketPolicy 的文档没有提供关于“根本没有超时”使用什么值的提示。我想如果我能算出这个值,我什至不需要更改我的代码,因为我可以使用 init 参数——我只是不知道要传递什么值。
    • 添加了更多关于空闲超时的回答
    • 最后connector.setIdleTimeout(-1); 是缺少的一行,我仍然可以使用Jetty9WebSocketFilter 并使用我在问题中指定的行来设置客户端超时。感谢您的澄清。
    猜你喜欢
    • 2015-06-24
    • 2017-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 2013-05-24
    • 2013-03-16
    • 1970-01-01
    相关资源
    最近更新 更多