代码之家  ›  专栏  ›  技术社区  ›  Mike

RequestHandler引发异常时Jetty正在重试请求

  •  0
  • Mike  · 技术社区  · 6 年前

    我注意到了 Jetty 正在重试对指定 Handler 无论何时 handle 引发异常。

    这是不受欢迎的行为,但我似乎找不到配置设置来阻止它。

    我已经使用Jetty一段时间了,但最近升级了我的环境以使用 9.4.9.v20180320 。这可能是也可能不是新的行为,但我从未注意到。

    下面是一个简单的用例来说明我的问题。请注意,输出中有两行指示正文内容,但令人困惑的是,在第二次尝试时,正文是空的。

    public class JettyTest extends AbstractHandler
    {
        @Override
        public void handle(String arg0, Request arg1, HttpServletRequest arg2, HttpServletResponse arg3)
        throws IOException
        {
            //read request body into string
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            //apache commons copy
            IOUtils.copy(arg1.getInputStream(), bos);
            String body = new String(bos.toByteArray());
            System.out.println("request body: " + body);
            throw new NullPointerException();
        }
    
        public static void main(String args[])
        throws Exception
        {
            Server server = new Server();
            server.setHandler(new JettyTest());
            LocalConnector localConnector = new LocalConnector(server);
            server.addConnector(localConnector);
            server.start();
            String simpleRequest = "GET / http/1.1\r\nHost: localhost:0\r\nContent-Type: text/plain\r\nContent-Length: 2\r\n\r\nhi";
            String response = localConnector.getResponse(simpleRequest);
            server.join();
        }
    
    }
    

    输出

    2018-03-26 19:47:49.590:INFO:oejs.Server:main: Started @327ms
    
    request body: hi
    
    2018-03-26 19:47:49.660:WARN:oejs.HttpChannel:qtp1349277854-12: /
    java.lang.NullPointerException
        at test.JettyTest.handle(JettyTest.java:28)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
        at org.eclipse.jetty.server.Server.handle(Server.java:531)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ByteArrayEndPoint$1.run(ByteArrayEndPoint.java:78)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)
        at java.lang.Thread.run(Thread.java:748)
    
    request body: 
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Joakim Erdfelt    6 年前

    你有很多事情要做。

    1. 您没有处理请求的权限(代码库中没有使用 Request.setHandled(true) )
    2. 您没有错误调度处理。
    3. 您没有ErrorHandler设置。

    所以发生的是。。。

    1. 您的JettyTest。调用handle(),则会流出异常。
    2. 请求现在处于错误调度模式。
    3. 错误调度请求现在再次发送到处理程序中进行错误处理。

    如果你改变一下。。。

    package jetty.errors;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    
    import javax.servlet.DispatcherType;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.eclipse.jetty.server.LocalConnector;
    import org.eclipse.jetty.server.Request;
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.handler.AbstractHandler;
    import org.eclipse.jetty.util.IO;
    
    public class HandlerWithError extends AbstractHandler
    {
        @Override
        public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException
        {
            if (DispatcherType.REQUEST.equals(httpServletRequest.getDispatcherType()))
            {
                // read request body into string
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                // apache commons copy
                IO.copy(httpServletRequest.getInputStream(), bos);
                String body = new String(bos.toByteArray());
                System.out.println("request body: " + body);
                throw new NullPointerException();
            }
            else
            {
                System.out.println("Now in DispatchType: " + httpServletRequest.getDispatcherType());
            }
        }
    
        public static void main(String args[])
                throws Exception
        {
            Server server = new Server();
            server.setHandler(new HandlerWithError());
            LocalConnector localConnector = new LocalConnector(server);
            server.addConnector(localConnector);
            server.start();
            String simpleRequest = "GET / http/1.1\r\n" +
                    "Host: localhost:0\r\n" +
                    "Connection: close\r\n" +
                    "Content-Type: text/plain\r\n" +
                    "Content-Length: 2\r\n\r\nhi";
            String response = localConnector.getResponse(simpleRequest);
            server.join();
        }
    }
    

    你会得到结果。。。

    2018-03-27 06:47:40.074:INFO::main: Logging initialized @429ms to org.eclipse.jetty.util.log.StdErrLog
    2018-03-27 06:47:40.148:INFO:oejs.Server:main: jetty-9.4.9.v20180320; built: 2018-03-20T07:21:10-05:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 9.0.4+11
    2018-03-27 06:47:40.182:INFO:oejs.AbstractConnector:main: Started LocalConnector@2f490758{HTTP/1.1,[http/1.1]}
    2018-03-27 06:47:40.183:INFO:oejs.Server:main: Started @548ms
    2018-03-27 06:47:40.287:WARN:oejs.HttpChannel:qtp2104545713-16: /
    java.lang.NullPointerException
        at jetty.errors.HandlerWithError.handle(HandlerWithError.java:29)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
        at org.eclipse.jetty.server.Server.handle(Server.java:531)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ByteArrayEndPoint$1.run(ByteArrayEndPoint.java:78)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)
        at java.base/java.lang.Thread.run(Thread.java:844)
    request body: hi
    Now in DispatchType: ERROR
    

    了解所有这些,您可以通过以下方式更改实现以利用此错误调度。

    package jetty.errors;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    
    import javax.servlet.DispatcherType;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.eclipse.jetty.http.HttpTester;
    import org.eclipse.jetty.server.LocalConnector;
    import org.eclipse.jetty.server.Request;
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.handler.AbstractHandler;
    import org.eclipse.jetty.server.handler.DefaultHandler;
    import org.eclipse.jetty.server.handler.HandlerList;
    import org.eclipse.jetty.util.IO;
    
    public class HandlerWithError
    {
        public static class MyRequestHandler extends AbstractHandler
        {
            @Override
            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
            {
                System.out.println("MyRequestHandler.handle() - DispatcherType: " + request.getDispatcherType());
                // only work with REQUEST Dispatches
                if (!DispatcherType.REQUEST.equals(request.getDispatcherType()))
                {
                    // skip this handler
                    return;
                }
    
                // Set handled (by this handler), don't let other handlers operate on this request
                baseRequest.setHandled(true);
                // read request body into string
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                // apache commons copy
                IO.copy(request.getInputStream(), bos);
                String body = new String(bos.toByteArray());
                System.out.println("request body: " + body);
                throw new NullPointerException();
            }
        }
    
        public static class MyErrorHandler extends AbstractHandler
        {
            @Override
            public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
            {
                System.out.println("MyErrorHandler.handle() - DispatcherType: " + request.getDispatcherType());
                if(!DispatcherType.ERROR.equals(request.getDispatcherType()))
                {
                    // skip this handler
                    return;
                }
    
                baseRequest.setHandled(true);
                response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
                response.setContentType("text/plain");
                response.getWriter().println("Go away, you silly NPE fool");
            }
        }
    
        public static void main(String args[])
                throws Exception
        {
            Server server = new Server();
            HandlerList handlers = new HandlerList();
            // Add your ERROR Dispatch handler first to handle dispatches for errors
            handlers.addHandler(new MyErrorHandler());
            // Add your handlers here (can be more then one)
            handlers.addHandler(new MyRequestHandler());
            // Always add DefaultHandler last, to ensure that something in your handler
            // list is calling baseRequest.setHandled(true)
            handlers.addHandler(new DefaultHandler());
            server.setHandler(handlers);
            LocalConnector localConnector = new LocalConnector(server);
            server.addConnector(localConnector);
            server.start();
    
            try
            {
                String simpleRequest = "GET / http/1.1\r\n" +
                        "Host: localhost:0\r\n" +
                        "Connection: close\r\n" +
                        "Content-Type: text/plain\r\n" +
                        "Content-Length: 2\r\n\r\nhi";
                HttpTester.Response response = HttpTester.parseResponse(
                        localConnector.getResponse(simpleRequest));
                System.out.println("Response: " + response);
                System.out.println(response.getContent());
            }
            catch (Throwable t)
            {
                t.printStackTrace();
            }
            finally
            {
                server.stop();
            }
        }
    }
    

    输出如下所示。。。

    2018-03-27 07:03:25.220:INFO::main: Logging initialized @320ms to org.eclipse.jetty.util.log.StdErrLog
    2018-03-27 07:03:25.288:INFO:oejs.Server:main: jetty-9.4.9.v20180320; built: 2018-03-20T07:21:10-05:00; git: 1f8159b1e4a42d3f79997021ea1609f2fbac6de5; jvm 9.0.4+11
    2018-03-27 07:03:25.309:INFO:oejs.AbstractConnector:main: Started LocalConnector@4ba2ca36{HTTP/1.1,[http/1.1]}
    2018-03-27 07:03:25.310:INFO:oejs.Server:main: Started @417ms
    MyErrorHandler.handle() - DispatcherType: REQUEST
    MyRequestHandler.handle() - DispatcherType: REQUEST
    request body: hi
    2018-03-27 07:03:25.399:WARN:oejs.HttpChannel:qtp525571-15: /
    java.lang.NullPointerException
        at jetty.errors.HandlerWithError$MyRequestHandler.handle(HandlerWithError.java:41)
        at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:61)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
        at org.eclipse.jetty.server.Server.handle(Server.java:531)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
        at org.eclipse.jetty.io.ByteArrayEndPoint$1.run(ByteArrayEndPoint.java:78)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:754)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:672)
        at java.base/java.lang.Thread.run(Thread.java:844)
    MyErrorHandler.handle() - DispatcherType: ERROR
    Response: HTTP/1.1 503 Service Unavailable
    Connection: close
    Content-Type: text/plain;charset=ISO-8859-1
    Content-Length: 29
    Server: Jetty(9.4.9.v20180320)
    
    Go away, you silly NPE fool
    
    2018-03-27 07:03:25.423:INFO:oejs.AbstractConnector:main: Stopped LocalConnector@4ba2ca36{HTTP/1.1,[http/1.1]}