繁体   English   中英

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

[英]Calling startAsync() on a servlet request throws IllegalStateException in a filter chain terminated by Jetty 9's ProxyServlet

环境:

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

问题:

“反向代理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版本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) 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()方法而引发了“ IllegalStateException”,这是第一次在我的过滤器中,第二次是在Jetty ProxyServlet的service()方法中? 如果是,是否没有办法在由Jetty的ProxyServlet终止的过滤器链中使用“ AsyncListener”?

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

仅供参考,我已修复此问题,如下所示:

  1. 我没有使用Jetty的“ ContinuationListener”,而是使用了Servlet 3.0的“ AsyncListener”,因为在升级到Jetty 9.3.14.v20161028之后,Jetty的“ ContinuationListener”似乎不起作用 这可能是一个Jetty错误。
  2. 与侦听器相关的代码段位于过滤器的“ chain.doFilter()”调用之前。 我将此代码段移至过滤器的“ chain.doFilter()”调用之后的位置,以便可以在Jetty ProxyServlet的service()方法中检索“已创建”的AsyncContext。 然后,我可以将AsyncListener添加到检索到的AsyncContext中。
  3. 我将过滤器中的“ 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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM