You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部署应用后出现ClientAbortException,需重启Tomcat恢复

部署应用后Tomcat出现ClientAbortException及NPE问题求助

问题描述

更新并上传应用变更后,立即出现以下错误,且问题会持续存在,直到重启Tomcat。已知该错误表示客户端与服务器连接已关闭,但此问题仅在部署应用时触发。

错误日志

29-07-2022@07:33:14 ERROR - Failed to invoke @ExceptionHandler method: public org.springframework.http.ResponseEntity<java.lang.Object> com.controller.ws.GlobalExceptionHandler.handleAllExceptions(java.lang.Exception,org.springframework.web.context.request.WebRequest)
org.apache.catalina.connector.ClientAbortException: java.io.IOException: Key no longer registered
    at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:309)
    at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:272)
    at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:118)
    at com.fasterxml.jackson.core.json.UTF8JsonGenerator.flush(UTF8JsonGenerator.java:1042)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2240)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:231)
    at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208)
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:161)
    at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:144)
    at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
    at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:362)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver.doResolveException(AbstractHandlerMethodExceptionResolver.java:60)
    at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:138)
    at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1167)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1004)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:955)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.core.filters.AuthFilter.doFilter(AuthFilter.java:128)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException: Key no longer registered
    at org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:87)
    at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:152)
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1253)
    at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:764)
    at org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:717)
    at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:707)
    at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.flush(Http11OutputBuffer.java:572)
    at org.apache.coyote.http11.filters.ChunkedOutputFilter.flush(ChunkedOutputFilter.java:157)
    at org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:220)
    at org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1195)
    at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:399)
    at org.apache.coyote.Response.action(Response.java:209)
    at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:305)
    ... 51 more
29-07-2022@08:32:12 ERROR - Failed to invoke @ExceptionHandler method: public org.springframework.http.ResponseEntity<java.lang.Object> com.controller.ws.GlobalExceptionHandler.handleAllExceptions(java.lang.Exception,org.springframework.web.context.request.WebRequest)
java.lang.NullPointerException
    at org.springframework.web.util.UrlPathHelper.getServletPath(UrlPathHelper.java:315)

全局异常处理器代码

@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> handleAllExceptions(final Exception ex, final WebRequest request) {

        return new ResponseEntity<Object>(new ErrorJsonObject(ApiResponseType.INVALID_ARGUMENT,
                "Client specified an invalid argument, request body or query param"), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(final Exception ex, final WebRequest request) {

        return new ResponseEntity<Object>(new ErrorJsonObject(ApiResponseType.INVALID_ARGUMENT,
                "Client specified an invalid argument, request body or query param"), HttpStatus.BAD_REQUEST);
    }

}

问题分析与修复方案

核心问题拆解

  1. ClientAbortException:部署时Tomcat的NIO连接池存在旧连接未被正确清理,当服务器尝试向已断开的客户端连接写响应时触发该错误。
  2. UrlPathHelper空指针:异常处理阶段请求对象已被销毁或状态异常,调用getServletPath时触发NPE。

具体修复步骤

1. 优化异常处理器,避免向已断开连接写响应

修改全局异常处理器,先识别客户端断开类异常,直接返回空响应,不执行序列化操作:

@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> handleAllExceptions(final Exception ex, final WebRequest request) {
        if (isClientDisconnected(ex)) {
            return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
        }
        return new ResponseEntity<>(new ErrorJsonObject(ApiResponseType.INVALID_ARGUMENT,
                "Client specified an invalid argument, request body or query param"), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(final Exception ex, final WebRequest request) {
        if (isClientDisconnected(ex)) {
            return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
        }
        return new ResponseEntity<>(new ErrorJsonObject(ApiResponseType.INVALID_ARGUMENT,
                "Client specified an invalid argument, request body or query param"), HttpStatus.BAD_REQUEST);
    }

    private boolean isClientDisconnected(Throwable ex) {
        while (ex != null) {
            if ((ex instanceof ClientAbortException) || 
                (ex instanceof IOException && ex.getMessage() != null && ex.getMessage().contains("Key no longer registered"))) {
                return true;
            }
            ex = ex.getCause();
        }
        return false;
    }
}

2. 调整Tomcat部署配置,清理旧连接

  • 启用优雅停机:在server.xml的NioEndpoint配置中添加stopGracefully="true",确保Tomcat等待现有连接处理完成后再关闭旧应用实例。
  • 缩短连接超时:在web.xml中设置会话超时,减少无效连接占用:
<session-config>
    <session-timeout>10</session-timeout>
</session-config>
  • 采用滚动部署:集群环境下逐个更新实例,避免全量切换导致连接异常。

3. 修复UrlPathHelper空指针

升级Spring版本到4.3.20+或5.0.9+,官方已修复该场景下的NPE;若无法升级,可在异常处理器中提前检查请求对象状态:

if (request instanceof ServletWebRequest) {
    HttpServletRequest servletRequest = ((ServletWebRequest) request).getRequest();
    if (servletRequest == null) {
        return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
    }
}

内容的提问来源于stack exchange,提问作者daniel_ip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 01:05:27