首页 文章

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

提问于
浏览
0

Environment:

  • 在我的环境中,Kibana 4.5.2在"reverse proxy servlet"后面运行,它是通过扩展Jetty的"ProxyServlet"类创建的 .

  • 这样做是为了可以使用URL https://Jetty_Server_IP:8443/visual-analytics/proxy/访问Kibana Web界面...此URL的请求被Jetty服务器中运行的反向代理拦截并重定向到https://localhost:5601/ ...即,到Kibana服务器是与Jetty Server在同一台机器上运行 .

  • 然后,Kibana Server处理Jetty服务器转发的请求,并将响应返回给Web浏览器 .

  • NOTE: Jetty在我的应用程序中的"embedded mode"中运行 .

Issue:

“反向代理servlet”映射到URL“/ visual-analytics / proxy / *” .

还有另一个“过滤器”映射到URL“/visual-analytics/proxy/elasticsearch/.kibana/search/*”,其中正在使用ContinuationListener,如以下代码段所示:

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);

上面的ContinuationListener与 Jetty version 8.1.15.v20140411 工作正常,并且正在调用侦听器的onComplete()方法 . 但是在将Jetty版本升级到 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);

但添加上面的“AsyncListener”实现会导致以下“IllegalStateException”:

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)
在javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
在javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
在javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
在org.eclipse.jetty.proxy.ProxyServlet.service(ProxyServlet.java:88)
在javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
在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)
在org.eclipse.jetty.servlet.ServletHandler $ CachedChain.doFilter(ServletHandler.java:1699)
at com.netiq.sentinel.kibana.proxy.AcceptEncodingHeaderModificationFilter.doFilter(AcceptEncodingHeaderModificationFilter.java:37)
在org.eclipse.jetty.servlet.ServletHandler $ CachedChain.doFilter(ServletHandler.java:1699)
在com.netiq.sentinel.kibana.proxy.SecurityFilter.doFilter(SecurityFilter.java:41)
在org.eclipse.jetty.servlet.ServletHandler $ CachedChain.doFilter(ServletHandler.java:1699)
at com.netiq.sentinel.kibana.proxy.AuditFilter.doFilter(AuditFilter.java:104)
在org.eclipse.jetty.servlet.ServletHandler $ CachedChain.doFilter(ServletHandler.java:1699)
在org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
在org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
在org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
在org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1180)
在org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
在org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
在org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
在org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
在org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119)
在org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
在org.eclipse.jetty.server.Server.handle(Server.java:534)
在org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
在org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
at org.eclipse.jetty.io.AbstractConnection $ ReadCallback.succeeded(AbstractConnection.java:273)
在org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
在org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:202)
at org.eclipse.jetty.io.AbstractConnection $ ReadCallback.succeeded(AbstractConnection.java:273)
在org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
在org.eclipse.jetty.io.SelectChannelEndPoint $ 2.run(SelectChannelEndPoint.java:93)
在org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
在org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
在org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
在org.eclipse.jetty.util.thread.QueuedThreadPool $ 2.run(QueuedThreadPool.java:589)
在java.lang.Thread.run(Thread.java:745)

上面的堆栈跟踪中提到的“ProxyServlet.java”中的第88行包含以下代码:

final AsyncContext asyncContext = request.startAsync();

抛出“IllegalStateException”是因为startAsync()方法在同一个请求上被调用了两次 - 这是我的过滤器中的第一次,也是Jetty ProxyServlet的service()方法中的第二次?如果是,是否无法在由Jetty的ProxyServlet终止的过滤器链中使用“AsyncListener”?

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

1 回答

  • 1

    仅供参考我修正了这个问题如下:

    • 而不是使用Jetty 's 360291 , I used Servlet 3.0' s "AsyncListener",因为Jetty的"ContinuationListener"在升级到 Jetty 9.3.14.v20161028 后似乎不起作用 . 这可能是一个Jetty错误 .

    • 与侦听器相关的代码段位于过滤器的“chain.doFilter()”调用之前 . 我将此代码段移动到过滤器的"chain.doFilter()"调用之后的位置,以便我可以在Jetty ProxyServlet的service()方法中检索"already created"的AsyncContext . 然后,我可以向检索到的AsyncContext添加AsyncListener .

    • 我将我的过滤器中的“request.startAsync()”调用更改为“request.getAsyncContext()”,这样我就不会启动导致IllegalStateException的新AsyncContext,而只检索已在Jetty的ProxyServlet中创建的AsyncContext .

    所以更新的代码段看起来像这样:

    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);
    

相关问题