Connection reset原因分析及解决思路
我们在开发过程中经常会出现Connection reset问题,包括http调用,数据库连接等场景。出现Connection reset的原因很多,本文从tcp层面简单介绍下Connection reset出现的原因,以及在实际开发过程中如何排查这类问题。
1、什么是Connection reset
在TCP首部中有6个标志位,其中一个标志位为RST,用于“复位”的。无论何时一个报文 段发往基准的连接( referenced connection)出现错误,TCP都会发出一个复位报文段。如果双方需要继续建立连接,那么需要重新进行三次握手建立连接。
2、出现RST的原因
2.1 RST攻击、干扰
上面简单介绍了RST标志位的作用,很容易想到这样一个场景,如果中间网络节点想要破坏一个tcp连接,那么它只要伪造成其中一方发送RST报文到另一方,即可让双方连接失效。
上图中,三次握手建立了tcp连接,由于是https连接,需要进行加密操作。这时,对方发送RST,双方并没有进行“四次挥手”,而是连接直接失效。这时客户端发起重拾,重新建立连接,但是很快又被“对方”重置了。和客户端连接tcp连接以及发送RST报文的,都不一定是真正的服务端,而可能是中间节点。我们使用HTTPS可以避免受到中间人攻击。对方无法使用中间人攻击,但是想阻止我们访问,所以可以通过RST来重置连接。
2.2 请求一个不存在的端口
当客户端访问服务端一个没有监听的端口时,服务端会发送RST报文。
如上图所示,客户端(192.168.2.192)访问了服务端(192.168.2.1)一个未监听的端口(9090),服务端发送了RST报文。
2.3 异常终止一个连接
终止一个连接的正常方式是一方发送 FIN。有时这也称为有序释放(orderly release),因为在所有排队数据都已发送之后才发送 FIN,正常情况下没有任何数据丢失。但也有可能发送一个复位报文段而不是 FIN来中途释放一个连接。有时称这为异常释放 (abortive release)。
上图,当客户端连接redis后,我们手动关闭redis服务,redis服务端会发送RST来强制终止一个连接。客户端通常收到这样的错误:Connection reset by peer,或者:远程主机强迫关闭了一个现有的连接。
2.4 半打开连接
如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的TCP连接称为半打开(Half-Open)的。只要不打算在半打开 连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。
3、排查思路
实际开发过程中,前面三个问题比较容易识别和解决。最困难的是最后一种半打开连接,原因往往很难发现。因为网络正常的情况下,都会通过正常关闭或者2.3的方式来关闭连接。现在客户端和服务端的网络非常复杂,有各种nat,代理,防火墙等设备,这些中间设备可能配置了一些安全策略,导致断开连接而不通知。
通过查询客户端日志,定位出现异常的时间。查询服务端日志,查询有无连接断开日志;查询连接是否存在;如果不存在,查询断开的时间。通过这些可以判断是不是由于半打开连接导致的问题。半打开连接一般是由于中间设备或者网络问题断开连接,而客户端不知道。
解决方案就是找到对应的设备,配置连接,避免长连接断开。
临时方案就是,开启keep-live;减少长连接存活时间;连接异常时进行重试。