问题解决
应用部署环境
- 语言:java
- 框架:ssm
- web容器:tomcat
- 负载:nginx
- 外层代理:F5
现象
根据客户需求对接一个停车缴费的功能,发布到生产环境之后发现,少量账单同时支付没有问题,一旦同时支付的账单数量超过某个值,就会出现网路连接问题,稳定复现。
解决
过程
首先查看了应用的日志,发现用户提示网络异常的时候,服务端没有任何相关的日志打印,确定请求没有发到服务端
查看Nginx Error日志发现打印了错误信息
2021/09/09 08:38:56 [error] 16299#16299: *240963 readv() failed (104: Connection reset by peer) while reading upstream, client: ****, server: ****, request: "POST ****?formData=E172Rfbkeuw2Z6fFYyg95hUMDmDwaOZT7Mqopwu07lo%3CVxsdDikPopy1XjjtjmvSusJwb7UF3erixZi5Wy099%3CewyDvM3wWhvE8X/z/vxKow2ttM1iHPSmWn...
通过nginx日志发现,虽然是nginx层抛出了错误,但是以日志内容来看,其实nginx已经是将请求的报文完整的接收了下来(这个也是在解决问题之后才反应过来),所以其实问题应该是出在Nginx将请求转给被代理的应用服务的时候。
当时在排查问题的时候,没有考虑到还有一层tomcat,导致哪怕是当时怀疑了问题不在nginx这块,还是不敢相信自己,去网上一顿乱搜。
最终解决
在tomcat/conf/server.xml中,增加Connector中的参数配置maxHttpHeaderSize="65536",增加允许tomcat接收的最大请求头大小
<Connector port="****" protocol="org.apache.coyote.http11.Http11NioProtocol"
URIEncoding="UTF-8"
maxHttpHeaderSize="65536"
connectionTimeout="20000"
acceptCount="500" maxThreads="500"
redirectPort="****" />
问题分析
连接重置
TCP RST
正常情况,服务端使用socket建立一个服务端监听,客户端通过socket向服务端监听发起连接, 双方经过TCP握手协议之后,数据开始传输,TCP协议规定连接在建立之后,双方只要有一端发起关闭的信号,两端就会走放手协议的流程(四次挥手),不再进行数据传输。但是如果一端发起关闭信号之后,不再接收请求,另外一端依然不进入关闭流程,而是依然不停的发送数据,或者是关闭的一端缓存区的数据没有读完就进行了关闭,这时候,关闭的一端就会返回一个RST的信号,告诉另外一端连接被重置
其他情况的RST
除了上边的一种情况,RST还可能出现在客户端找不到服务端端口,服务端因为各种关闭不接收数据等等场景中,但是无一例外,最终就是一端的数据,没有被另外一端完整读取到 ,比如以下几种情况
- 客户端直接找不到想要连接的服务端
- 一端早就处于关闭的状态了,另外一端还在傻乎乎的给他传输数据
- 一端关闭的时候,没有读完另外一端发过来的数据
Tomcat 的 Connector
其实在一定程度上说,Tomcat和Nginx的作用相同,只不过两者的职责不同,Nginx使用了异步非阻塞高性能的组合,可以代理各种各样的URI资源,而Tomcat代理的是一个一个的Servlet容器,它可以容纳所有遵循Servlet规范的应用,并且统一将它们管理。Connector是其中最重要的一部分,它是一个HTTP连接器,它通过启动一个Socket监听,用来接收不同类型的请求,然后把他们解析成对应的Servlet规范的请求,才会将这些请求分发到不同的Servlet中进行处理。当然,内部做了很多其他的事情包括请求校验拦截,请求转化,请求异步线程处理等等。这里只是简单介绍一下,后续会增加关于tomcat部分的文章
Nginx 104
在我们这个案例的场景下分析,nginx要将拿到的请求转发给tomcat中的应用,需要跟tomcat的Connector建立连接,可以将nginx理解为客户端,将tomcat中的Connector理解为socket服务端。tomcat给Connector一套默认的配置,其中maxHttpHeaderSize默认的值是4096字节,也就是4kb。超过4kb的请求头大小的请求,不进行处理,当然这里也有可能发生两种情况,第一种是Connector一开始就知道nginx发过来的请求头过大,直接不接收,响应回去RST标识,还有一种是Connector没有管请求头的大小,直接去接收,但是因为没有将请求头数据读取完就关闭了,响应了RST。这部分没有细看,但是不论怎么说,都是因为上边说过的,没有正常处理完客户端发送过来所有的数据。
类似问题解决思路
在开始无脑查询的时候,其实有很多答案虽然错误码是104,但是报错的原因是不相同的,解决方案也是各不相同,看到过大概以下几种解决思路
- nginx的buffer太小,timeout太小。
- 长连接,增加长连接超时时间
- 将 http version改到1.1 (其实也是使用长连接解决,因为http1.1默认使用长连接)
虽然个人试其他解决方式的时候,都没有成功,也有可能是因为tomcat Connector 连接器的最大请求头4K大小的这个默认配置从最基础的环节直接给把其他配置砍掉了。但是不论使用何种方式解决,最终来说我们就一个思路(虽然说了很像没说),先找到是哪端没有将数据读取完毕,然后想办法让它正常读取
总结
本片文章根据个人发生的实际生产问题,着手解决并且进行问题分析,通过对nginx104的跟踪,对连接重置的概念有一个更详细的了解。
到此这篇关于Nginx报错104:Connection reset by peer问题的解决及分析的文章就介绍到这了,更多相关Nginx报错104:Connection reset by peer内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!