博主介紹:✌全網粉絲22W+,博客專家、Java領域優質創作者,華為雲/阿里雲等平台優質作者、專注於Java技術領域✌

技術範圍:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大數據、物聯網、機器學習等設計與開發。

感興趣的可以先關注收藏起來,在工作中、生活上等遇到相關問題都可以給我留言諮詢,希望幫助更多的人。

org.springframework.web.context.request.async.AsyncRequestNotUsableException: ServletOutputStream failed to flush: java.io.IOException: Broken pipe 異常解決

  • 一、報錯內容
  • 二、原因分析
  • 2.1 直接原因
  • 2.2 觸發場景
  • 2.3 Spring 異步機制
  • 三、解決方案
  • 3.1 捕獲異常並忽略(推薦)
  • 3.2 設置超時時間
  • 3.3 檢查響應是否可寫
  • 3.4 調整服務器配置
  • 四、根本問題排查
  • 五、總結

一、報錯內容

以下是報錯內容詳情:

org.springframework.web.context.request.async.AsyncRequestNotUsableException: ServletOutputStream failed to flush: java.io.IOException: Broken pipe
        at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleHttpServletResponse.handleIOException(StandardServletAsyncWebRequest.java:320)
        at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleServletOutputStream.flush(StandardServletAsyncWebRequest.java:392)
        at java.base/java.io.FilterOutputStream.flush(FilterOutputStream.java:153)
        at com.fasterxml.jackson.core.json.UTF8JsonGenerator.flush(UTF8JsonGenerator.java:1200)
        at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1063)
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:483)
        at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:114)
        at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:297)
        at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:192)
        at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:136)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:904)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
        at java.base/java.lang.Thread.run(Thread.java:842)
Caused by: org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:303)
        at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:265)
        at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:136)
        at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleServletOutputStream.flush(StandardServletAsyncWebRequest.java:389)
        ... 52 common frames omitted
Caused by: java.io.IOException: Broken pipe
        at java.base/sun.nio.ch.FileDispatcherImpl.write0(Native Method)
        at java.base/sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:62)
        at java.base/sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:132)
        at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:97)
        at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:53)
        at java.base/sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:532)
        at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:122)
        at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1378)
        at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:764)
        at org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:728)
        at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:712)
        at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.flush(Http11OutputBuffer.java:559)
        at org.apache.coyote.http11.filters.ChunkedOutputFilter.flush(ChunkedOutputFilter.java:157)
        at org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:216)
        at org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1244)
        at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:400)
        at org.apache.coyote.Response.action(Response.java:208)
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:299)
        ... 55 common frames omitted

這個錯誤 org.springframework.web.context.request.async.AsyncRequestNotUsableException: ServletOutputStream failed to flush: java.io.IOException: Broken pipe 通常發生在 Spring Web 異步請求處理 過程中,客户端(如瀏覽器、移動端或 API 調用方)在服務器尚未完成響應時提前關閉了連接(如刷新頁面、取消請求或網絡中斷),導致服務器嘗試寫入數據時發現連接已斷開(Broken pipe)。

二、原因分析

2.1 直接原因

客户端主動斷開連接(TCP RST 包),但服務器仍在嘗試通過 ServletOutputStream 寫入數據。

2.2 觸發場景

  • 長輪詢(Long Polling)或 SSE(Server-Sent Events)請求被客户端取消。
  • 前端頁面跳轉或用户手動刷新。
  • 網絡不穩定(如移動端切換網絡)。
  • 服務器響應超時,客户端主動超時斷開。

2.3 Spring 異步機制

當使用 @Async、DeferredResult 或 Callable 時,Spring 會在獨立線程中處理請求。如果客户端斷開,服務器線程可能仍在執行,最終嘗試刷新響應時拋出此異常。

三、解決方案

3.1 捕獲異常並忽略(推薦)

如果是非關鍵業務(如日誌、通知推送),可以直接捕獲異常並忽略:

import org.springframework.web.context.request.async.AsyncRequestNotUsableException;

@RestController
public class AsyncController {
    @GetMapping("/async-data")
    public DeferredResult<String> getAsyncData() {
        DeferredResult<String> deferredResult = new DeferredResult<>();
        
        new Thread(() -> {
            try {
                // 模擬耗時操作
                Thread.sleep(3000);
                deferredResult.setResult("Data loaded!");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (AsyncRequestNotUsableException e) {
                // 客户端已斷開,忽略異常
                System.out.println("Client disconnected: " + e.getMessage());
            }
        }).start();
        
        return deferredResult;
    }
}

3.2 設置超時時間

為 DeferredResult 設置超時,避免長時間佔用資源:

@GetMapping("/async-data")
public DeferredResult<String> getAsyncData() {
    DeferredResult<String> deferredResult = new DeferredResult<>(5000L); // 5秒超時
    
    deferredResult.onTimeout(() -> {
        System.out.println("Request timeout");
    });
    
    deferredResult.onError((Throwable t) -> {
        if (t instanceof AsyncRequestNotUsableException) {
            System.out.println("Client disconnected");
        }
    });
    
    // 異步處理邏輯...
    return deferredResult;
}

3.3 檢查響應是否可寫

在寫入響應前檢查連接狀態:

if (!deferredResult.isSetOrExpired()) {
    deferredResult.setResult("Data");
} else {
    System.out.println("Response already closed");
}

3.4 調整服務器配置

  • Tomcat:在 application.properties 中調整連接超時:
server.tomcat.connection-timeout=5000 # 5秒
  • Undertow:禁用異步請求的 Broken pipe 日誌:
server.undertow.no-request-timeout=5000

四、根本問題排查

  • 客户端行為:

檢查前端代碼是否意外取消請求(如 axios 的 CancelToken)。

確認網絡穩定性(特別是移動端)。

  • 服務器性能:

如果服務器處理過慢,優化異步任務邏輯(如數據庫查詢、外部 API 調用)。

使用熔斷機制(如 Hystrix 或 Resilience4j)避免長時間阻塞。

  • 日誌監控:

記錄完整的異常堆棧,分析斷開時的上下文:

catch (AsyncRequestNotUsableException e) {
    log.error("Client disconnected during async processing", e);
}

五、總結

方案

適用場景

捕獲並忽略異常

非關鍵任務(如通知推送)

設置超時和回調

需要資源控制的場景

檢查響應狀態

避免重複寫入已關閉的連接

調整服務器配置

減少不必要的錯誤日誌

如果問題持續,建議結合 APM 工具(如 SkyWalking、New Relic)監控異步請求的生命週期,定位客户端斷開的具體原因。



好了,今天分享到這裏。希望你喜歡這次的探索之旅!不要忘記 “點贊” 和 “關注” 哦,我們下次見!🎈

本文完結!