我們在開發過程中經常會出現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;減少長連接存活時間;連接異常時進行重試。
