在 servlet 请求上调用 startAsync() 会在由 Jetty 9 的 ProxyServlet 终止的过滤器链中抛出 IllegalStateException。



环境:

  • 在我的环境中,Kibana 4.5.2在"反向代理servlet"后面运行,该"反向代理servlet"是通过扩展Jetty的" Proxyservlet"类创建的。
  • 这样做是为了使Kibana Web界面可以使用URL https://jetty_server_ip:8443/visual-analytics/proxy/...此URL的请求截取是由在Jetty Server中运行的反向代理拦截的请求并将重定向到https://localhost:5601/...即,到与Jetty Server在同一机器中运行的Kibana服务器。
  • 然后,Kibana服务器处理码头服务器转发的请求,并将响应返回到Web浏览器中。
  • 注意:码头在我的应用程序中以"嵌入式模式"运行。

问题:

"反向代理servlet"映射到URL/Visual-Analytics/proxy/*"。

还有另一个"过滤器"映射到URL"/visual-analytics/proxy/elasticsearch/.kibana/search/*"其中,其中continuationListener被以下代码snippet所示:

    ContinuationSupport.getContinuation(myRequestWrapper).addContinuationListener(new ContinuationListener() {
                        @Override
                        public void onTimeout(Continuation continuation) {
                            logger.log(Level.WARNING, "Request timeout...");
                        }
                        @Override
                        public void onComplete(Continuation continuation) {
                            HttpServletResponse httpResponse = (HttpServletResponse)continuation.getServletResponse();
                            if (httpResponse.getStatus() == HttpServletResponse.SC_OK || httpResponse.getStatus() == HttpServletResponse.SC_CREATED ) {
                                   //some business logic
                            }
                        }
                    });
    chain.doFilter(myRequestWrapper, response);

上面的连续列表与 jetty版本8.1.15.v20140411 和侦听器的oncomplete()方法都可以正常工作。但是,将码头版本升级到 9.3.14.v20161028 之后,continuationListener不再工作,即,侦听器的oncomplete()方法都不被调用ontimeout()方法。

由于ContinuationListener不再工作,因此我用以下代码片段指示的" Javax.servlet.asynclistener"实现了它:

                AsyncContext asyncContext = myRequestWrapper.startAsync();
                asyncContext.addListener(new AsyncListener() {
                    @Override
                    public void onTimeout(AsyncEvent event) throws IOException 
                    {
                                logger.log(Level.WARNING, "Async timeout...");
                    }
                    @Override
                    public void onStartAsync(AsyncEvent event) throws IOException 
                    {
                                logger.log(Level.INFO, "Async start...");
                    }
                    @Override
                    public void onError(AsyncEvent event) throws IOException 
                    {
                                logger.log(Level.SEVERE, "Async error...");
                    }
                    @Override
                    public void onComplete(AsyncEvent event) throws IOException 
                    {
                          HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();
                         //HttpServletResponse httpResponse = (HttpServletResponse) event.getAsyncContext().getResponse();
                        if (httpResponse.getStatus() == HttpServletResponse.SC_OK || httpResponse.getStatus() == HttpServletResponse.SC_CREATED ) {
                                    //some business logic
                        }
                    }
        }, myRequestWrapper, httpServletResponse);
        chain.doFilter(myRequestWrapper, response);
   https://164.99.175.139:8443/visual-analytics/proxy/elasticsearch/.kibana/search/test1?op_type=create
    java.lang.IllegalStateException: s=DISPATCHED i=true a=STARTED
            at org.eclipse.jetty.server.HttpChannelState.startAsync(HttpChannelState.java:264)
            at org.eclipse.jetty.server.Request.startAsync(Request.java:2235)
            at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
            at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
            at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
            at org.eclipse.jetty.proxy.ProxyServlet.service(ProxyServlet.java:88)
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
            at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845)
            at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1712)
            at com.netiq.sentinel.kibana.search.KibanaSearchFilter.doFilter(KibanaSearchFilter.java:249)
            at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1699)
            at com.netiq.sentinel.kibana.proxy.AcceptEncodingHeaderModificationFilter.doFilter(AcceptEncodingHeaderModificationFilter.java:37)
            at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1699)
            at com.netiq.sentinel.kibana.proxy.SecurityFilter.doFilter(SecurityFilter.java:41)
            at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1699)
            at com.netiq.sentinel.kibana.proxy.AuditFilter.doFilter(AuditFilter.java:104)
            at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1699)
            at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
            at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
            at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
            at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
            at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1180)
            at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
            at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
            at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
            at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
            at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
            at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119)
            at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
            at org.eclipse.jetty.server.Server.handle(Server.java:534)
            at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
            at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
            at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
            at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
            at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:202)
            at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
            at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
            at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
            at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
            at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
            at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
            at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
            at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
            at java.lang.Thread.run(Thread.java:745)

" proxyservlet.java"中的行号88,上述堆栈跟踪中提到的是以下代码:

final AsyncContext asyncContext = request.startAsync();

是因为startAsync()方法在同一请求中被调用两次,这是我的过滤器中的第一次,并且在Jetty Proxyservlet的Service()方法中第二次被称为" IllegalStateException"?如果是,是否没有办法在由Jetty的ProxyServlet终止的过滤链中使用" rasynclistener"?

关于如何进一步进行的任何指针将不胜感激。期待任何回应...

fyi我修复了以下问题:

  1. 我不使用Jetty的" ContinuationListener",而是使用了Servlet 3.0的" Asynclistener",因为Jetty的" ContinuationListener"似乎在升级到 JETTY 9.3.14.v20161028 之后似乎不起作用。这可能是一个码头错误。
  2. 侦听器相关的代码段位于过滤器的"链"呼叫之前。我将此代码段移至过滤器的"链" chain.dofilter()"调用后,以便我可以在Jetty ProxyServlet的Service()方法中检索已"已创建"的AsyncContext。然后,我可以在检索的Asynconcontext中添加异步。
  3. 我将过滤器中的" request.startasync()"更改为" request.getAsyncconconcext()",这样我就不会启动一个新的asynccontext,这会导致IllegalStateException,但只能检索已经在Asynccontext中创建的asynccontextJetty的Proxyservlet。

因此,更新的代码段看起来像这样:

            chain.doFilter(myRequestWrapper, response);
            AsyncContext asyncContext = myRequestWrapper.getAsyncContext();
            asyncContext.addListener(new AsyncListener() {
                @Override
                public void onTimeout(AsyncEvent event) throws IOException 
                {
                            logger.log(Level.WARNING, "Async timeout...");
                }
                @Override
                public void onStartAsync(AsyncEvent event) throws IOException 
                {
                            logger.log(Level.INFO, "Async start...");
                }
                @Override
                public void onError(AsyncEvent event) throws IOException 
                {
                            logger.log(Level.SEVERE, "Async error...");
                }
                @Override
                public void onComplete(AsyncEvent event) throws IOException 
                {
                      HttpServletResponse httpResponse = (HttpServletResponse) event.getSuppliedResponse();
                     //HttpServletResponse httpResponse = (HttpServletResponse) event.getAsyncContext().getResponse();
                    if (httpResponse.getStatus() == HttpServletResponse.SC_OK || httpResponse.getStatus() == HttpServletResponse.SC_CREATED ) {
                                //some business logic
                    }
                }
    }, myRequestWrapper, httpServletResponse);

最新更新