Tcp keepalive 引发的 Connection reset by peer 异常

不想你离开。 提交于 2019-12-26 19:32:42

业务需要,为了提供高性能的查询服务,引入了一款高速搜索服务,客户端的链接使用的HTTP,测试环境使用一切正常,但上线后几乎每天都会发生异常Connection reset by peer。

具体场景如下:

两台业务服务器,用客户端(基于HTTPClient,实现了长连接)连接的搜索服务集群(集群有三台机器),客户端与服务器分别部署在不同的网段,异常会有规律的出现: 每天9点左右会发生异常Connection reset by peer. 而且是连续有三个

Connection reset by peer Caused by: java.io.IOException: Connection reset by peer         

at sun.nio.ch.FileDispatcherImpl.read0(Native Method)         

at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)         

at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)         

at sun.nio.ch.IOUtil.read(IOUtil.java:197)

这个异常到是很好解决,引入重试机制,捕获这个异常后重新发起请求,第二次都会成功,但这是治标不治本,还需要进一步探究问题的根源。

于是乎,查官方文档、比对代码、让运维同事帮着抓包。。。想了多种方法,经过若干天的努力,最终发现这个异常是和keepalive这个关键词相关。

keepalive 分两种:

Http的 keep-alive参数Tcp keepalive配置

HTTP1.1中默认启用"Connection: Keep-Alive",表示这个HTTP连接可以复用,下次的HTTP请求就可以直接使用当前连接,从而提高性能,一般HTTP连接池实现都用到keep-alive,这个一般是默认选项;

​TCP的keepalive的作用和HTTP中的不同,TPC中主要用来实现连接保活,相关配置主要是net.ipv4.tcp_keepalive_time这个参数,表示如果经过多长时间(默认2小时)一个TCP连接没有交换数据,就发送一个心跳包,探测下当前链接是否有效,正常情况下会收到对方的ack包,表示这个连接可用。

实际线上环境,业务服务器和搜索服务集群之间有一道防火墙,而防火墙策略定义空闲连接超时时间为1小时,与上面提到的linux服务器默认的2小时不一致。

由于我们当前系统晚上访问量较少,导致某些连接超过2小时没有使用,在其中1小时后防火墙自动就终止了当前连接,到了2小时后服务器尝试发送心跳保活连接,直接被防火墙拦截,若干次尝试后服务端发送RST信号中断了链接,而此时的客户端并不知情;当第二天早上使用这个失效的链接请求时,服务端直接返回RST,客户端报错Connection reset by peer,尝试了集群中的三台服务器都返回同样错误,所以连续报了3个相同的异常。解决方案也比较简单,修改服务端keepalive超时配置,小于防火墙的1小时即可。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!