近期上线了一个 Spring Boot 前后端不分离的一体式项目。在本地测试的时候,项目正常运行,没有任何报错,部署到服务器上时,出现了这样离奇的问题:

  • 使用 ip:port 访问项目时,可以正常访问,日志内没有任何报错。
  • Nginx 配置反向代理,通过域名访问项目时,可以正常访问,但是日志内出现了报错。

报错信息如下:

2021-06-02 15:39:15.316 ERROR 19196 --- [io-7797-exec-91] c.x.j.a.c.resolver.WebExceptionResolver  : WebExceptionResolver:{}

org.apache.catalina.connector.ClientAbortException: java.io.IOException: Connection reset by peer
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:351) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.connector.OutputBuffer.flushByteBuffer(OutputBuffer.java:776) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
2021-06-02 15:39:15.318 ERROR 19196 --- [io-7797-exec-95] c.x.j.a.c.resolver.WebExceptionResolver  : WebExceptionResolver:{}

org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:351) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]
    at org.apache.catalina.connector.OutputBuffer.flushByteBuffer(OutputBuffer.java:776) ~[tomcat-embed-core-9.0.41.jar!/:9.0.41]

虽然在 Nginx 反向代理的场景下日志文件有报错信息,但是项目又可以正常运行。但是长此以往日志文件就会越积越多,能解决一个报错是一个报错。

经过两天的百度搜索,能得到一些统一的答案:网络异常、提前关闭,很多博客说得云里雾里的。大部分博客对于这个问题的解决方案,要么都是复制粘贴,重复内容一大堆,要么就是一点实质性的东西都没有,终究还是得自己摸索。

先总结一下百度找到的几个原因和解决方案:

林子大了什么鸟都有,代码写多了什么 BUG 都有。

出现这个问题之后,我仔细分析了一下,可能是 Nginx 反向代理配置的问题。参考搜索结果,修改 Nginx 缓存等等等乱七八糟的反向代理配置,毫无作用。

想到异常信息中出现了 java.io.IOException 这个字眼,我想会不会是因为某个文件请求出问题了。因此我在程序中加了个自定义异常处理的操作,捕获一下这个异常产生时所请求的 URL 和请求参数相关信息。

自定义异常处理做好之后,我又在反向代理的环境下访问了一次项目。自定义异常输出的内容大概是:

请求链接:xxxxx/xxx.png(是个图片)
异常名称:org.apache.catalina.connector.ClientAbortException: java.io.IOException: Connection reset by peer

由此可见:由于静态资源的的未知原因,在反向代理的环境下访问静态资源,会报出这个异常,但是静态资源能正常加载。

解决方法:Nginx 反向代理时,针对静态资源单独设置规则。

location ~ .*.(gif|jpg|png|html|htm|css|js|ico|swf|pdf|ttf|woff|woff2)$ {
    proxy_redirect off;
    proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header;
    proxy_set_header Host $host;
    proxy_set_header X-real-ip $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:8081;

    #Use Proxy Cache
    #Set Nginx Cache
    proxy_ignore_headers Set-Cookie Cache-Control expires;
    add_header Cache-Control no-cache;
    proxy_cache_valid  any 2d;
}

单独对静态资源下手后,问题解决,至此也没有再爆出这些错误。

至于问题出现的原因,有可能是因为 Nginx 反向代理缓存配置的原因,也有可能是其他未知原因。但是能确定的是,反向代理过程中某一端提前关闭请求,才会造成 java.io.IOException: Broken pipe。深层原因就有待深究了。

反正就是离谱。