【问题标题】:ClientAbortException when using Jersey 2.13使用 Jersey 2.13 时出现 ClientAbortException
【发布时间】:2014-11-05 08:47:42
【问题描述】:

我在我的 Web 应用程序中使用 Jersey 2.13 来异步检索数据。在某些情况下,请求需要一些时间(即执行复杂报告时)直到其响应返回给客户端。

当客户端不等待异步响应(离开页面、关闭浏览器等)时,会抛出 ClientAbortException。这种行为符合预期,但它使我的日志文件充满了堆栈跟踪,因为在响应返回之前被取消的每个异步请求都会打印一个堆栈跟踪。

堆栈跟踪如下所示:

Oct 15, 2014 2:25:23 PM org.glassfish.jersey.server.ServerRuntime$Responder writeResponse
SEVERE: An I/O error has occurred while writing a response message entity to the container output stream.
org.glassfish.jersey.server.internal.process.MappableException: ClientAbortException:  java.io.IOException
                at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:91)
                at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
                at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1154)
                at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:621)
                at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:377)
                at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:367)
                at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:274)
                at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
                at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
                at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
                at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
                at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
                at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:297)
                at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:254)
                at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1030)
                at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:373)
                at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:381)
                at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:344)
                at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:221)
                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
                at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
                at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
                at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
                at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
                at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
                at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
                at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
                at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
                at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:409)
                at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1044)
                at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
                at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2441)
                at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2430)
                at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
                at java.lang.Thread.run(Unknown Source)
Caused by: ClientAbortException:  java.io.IOException
                at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:413)
                at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:480)
                at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:366)
                at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:438)
                at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:426)
                at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:91)
                at org.glassfish.jersey.servlet.internal.ResponseWriter$NonCloseableOutputStreamWrapper.write(ResponseWriter.java:298)
                at org.glassfish.jersey.message.internal.CommittingOutputStream.write(CommittingOutputStream.java:229)
                at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$UnCloseableOutputStream.write(WriterInterceptorExecutor.java:299)
                at com.fasterxml.jackson.core.json.UTF8JsonGenerator._flushBuffer(UTF8JsonGenerator.java:1862)
                at com.fasterxml.jackson.core.json.UTF8JsonGenerator.close(UTF8JsonGenerator.java:1087)
                at com.fasterxml.jackson.jaxrs.base.ProviderBase.writeTo(ProviderBase.java:637)
                at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:265)
                at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:250)
                at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
                at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:106)
                at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
                at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:85)
                ... 38 more
Caused by: java.io.IOException
                at org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer(InternalAprOutputBuffer.java:205)
                at org.apache.coyote.http11.InternalAprOutputBuffer.access$100(InternalAprOutputBuffer.java:37)
                at org.apache.coyote.http11.InternalAprOutputBuffer$SocketOutputBuffer.doWrite(InternalAprOutputBuffer.java:235)
                at org.apache.coyote.http11.filters.ChunkedOutputFilter.doWrite(ChunkedOutputFilter.java:117)
                at org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:192)
                at org.apache.coyote.Response.doWrite(Response.java:517)
                at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:408)
                ... 55 more

我也在使用 Jersey ExceptionMapper 来映射几个异常,但这对

org.glassfish.jersey.server.internal.process.MappableException

也不为

org.apache.catalina.connector.ClientAbortException

有什么方法可以捕获这个异常并阻止打印整个堆栈跟踪?

编辑:

仍在寻找答案...

【问题讨论】:

  • 我对此也很感兴趣。我不仅得到一个 Jersey 堆栈跟踪,而且它把它扔到容器(tomcat),我得到一个容器堆栈跟踪
  • 我可以帮你。您能否共享端点代码/客户端代码以重现该错误。谢谢。

标签: java rest jersey-2.0


【解决方案1】:

我通过添加低优先级WriterInterceptor 来解决此问题,该WriterInterceptor 检测并忽略写入响应时引发的异常。如果您在 Tomcat 上运行并且不介意依赖于 Tomcat 类,则可以使用 org.apache.catalina.connector.ClientAbortException 而不是调用 setOutputStream,这将消除对两个嵌套类的需要(以及对 org.apache.commons.io.output.ProxyOutputStream 的依赖,这也可以使用自定义 OutputStream 子类轻松避免)。

import java.io.IOException;
import java.io.OutputStream;

import javax.annotation.Priority;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;

import org.apache.commons.io.output.ProxyOutputStream;

/**
 * Ignore exceptions when writing a response, which almost always means the
 * client disconnected before reading the full response.
 */
@Provider
@Priority(1)
public class ClientAbortExceptionWriterInterceptor implements WriterInterceptor {
    @Override
    public void aroundWriteTo(WriterInterceptorContext context) throws IOException {
        context.setOutputStream(new ClientAbortExceptionOutputStream(context.getOutputStream()));
        try {
            context.proceed();
        } catch (Throwable t) {
            for (Throwable cause = t; cause != null; cause = cause.getCause()) {
                if (cause instanceof ClientAbortException) {
                    return;
                }
            }
            throw t;
        }
    }

    private static class ClientAbortExceptionOutputStream extends ProxyOutputStream {
        public ClientAbortExceptionOutputStream(OutputStream out) {
            super(out);
        }

        @Override
        protected void handleIOException(IOException e) throws IOException {
            throw new ClientAbortException(e);
        }
    }

    @SuppressWarnings("serial")
    private static class ClientAbortException extends IOException {
        public ClientAbortException(IOException e) {
            super(e);
        }
    }
}

【讨论】:

    【解决方案2】:

    我也遇到过这个问题,终于找到了“解决”这个问题的指南。

    https://tutorial-academy.com/jersey-workaround-clientabortexception-ioexception/

    有两个选项,第一个是当前接受的答案。另一种首选方法是添加 WriterInterceptor 以删除 ClientAbortException。我个人的转折是 WARN 记录此事件。

    如果 URL 无法访问,我会在此处添加我的实现。 别忘了在你的 Jersey 环境中注册它。

    import org.apache.catalina.connector.ClientAbortException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.annotation.Priority;
    import javax.ws.rs.ext.Provider;
    import javax.ws.rs.ext.WriterInterceptor;
    import javax.ws.rs.ext.WriterInterceptorContext;
    import java.io.IOException;
    
    
    @Provider
    @Priority(1)
    public class ClientAbortExceptionWriterInterceptor implements WriterInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(ClientAbortExceptionWriterInterceptor.class);
    
    @Override
    public void aroundWriteTo(WriterInterceptorContext context) throws IOException {
        try {
            context.proceed();
        } catch (Throwable t) {
            for (Throwable cause = t; cause != null; cause = cause.getCause()) {
                if (cause instanceof ClientAbortException) {
                    logger.warn("Client aborted request.", cause);
                    return;
                }
            }
            throw t;
        }
    }
    

    }

    【讨论】:

      【解决方案3】:

      感谢@Chip,它也可以将代码直接放在 WS 中。

      @Path("/myWS")
      public class MyWS {   
      
          private final static Logger ORG_GLASSFISH_JERSEY_LOGGER = Logger.getLogger("org.glassfish.jersey");
          static {
              ORG_GLASSFISH_JERSEY_LOGGER.setLevel(Level.OFF);
          }    
      
          @GET
          @Produces(MediaType.APPLICATION_JSON)
          @Path("/version")
          public String version() {
              return "1.0.25";
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        在深入研究 Jersey Coding 之后,我发现存档的唯一方法是禁用 Jersey 内部 Logger。这可以在扩展 ResourceConfig 的类中完成。

        @ApplicationPath("api")
        public class Application extends ResourceConfig {
        
            private final static Logger ORG_GLASSFISH_JERSEY_LOGGER = Logger
                    .getLogger("org.glassfish.jersey");
            static {
                ORG_GLASSFISH_JERSEY_LOGGER.setLevel(Level.OFF);
            }
        }
        

        【讨论】:

        • 同意,关闭内部 Jersey 记录器似乎是一个糟糕的解决方案。
        猜你喜欢
        • 1970-01-01
        • 2015-12-29
        • 2012-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多