前段時間和其它系統做聯調測試,對方系統采用的是負載均衡模式。調試時采用的是多台手機作為客戶端發送到對方負載均衡服務器,然后再把報文轉發送到我這邊的服務端。在測試的時候,對方測試人員說有的手機客戶端會偶爾出現報文發不過來的情況。
故事有點長,先發一張tcp三次握手的過程圖鎮樓~

1 自己服務端的socket監聽出現問題
一開始認為可能是自己作為服務端的監聽有問題,因為后面排查監聽端口的時候發現了close_wait的情況。當時沒多想,認為對方負載均衡不會出錯(先前跟其它系統聯調過了),就急着解決close_wait的問題去了。
可是后面測試的時候,盡管服務端監聽沒有任何異常,但是手機APP還是有發包失敗的情況,而且怪異的是服務端日志也沒打印請求包內容。
2 防火牆只收到對方系統的一個IP記錄
折騰了很久還是沒找到原因所在,后面聯系了對方系統測試人員得到回復說他們的日志報錯:
java.net.SocketTimeoutException: SocketTimeoutException invoking https://123.123.123.214:7070: connect timed out
於是聯系網絡管理員,看防火牆是否拒掉了對方請求報文。結果網管回復防火牆正常,但是只收到對方的一台IP記錄,另一IP沒有發送過報文。
立即反映給對方開發人員,結果對方發現是負載均衡系統的一台服務器連接我這邊系統的網絡有問題。
3 問題的總結
到這里問題已經解決了,但是自己對於tcp出現Connection timed out的錯誤認識不足,只想到是自己服務端close_wait引起的問題。下面是自己對tcp握手過程中出現Connection refused和Connection timed out的總結。
3.1 Connection refused
使用telnet來檢查tcp鏈路時,如果遇到"Connection refused"的錯誤,那么表示從本地客戶端到目標IP地址的路由是正常的,但是該目標端口沒有進程在監聽,然后服務端拒絕掉了連接。
一個成功的tcp鏈接將會看到Syn,Syn-Ack,Ack,這也就是我們預期的TCP三次握手。當使用tcpdump或wireshark抓包工具來探測發送過來的請求報文包時,Connection refused將會看到Syn,Rst。
3.2 Connection timed out
如果telnet的時候,TCP路由不正常,那么會得到一個Connection timed out的錯誤。"Couldn't connect"原因有很多,可能是服務器無法ping通,可能是服務器(防火牆等)丟棄了該請求報文包,也可能是服務器應答太慢,又或者存在間歇性的問題(這種情況很難從日志文件中排查問題)。
下面演示“Connection timed out”的情況:
# 先打開一個ssh會話,telnet任意一個不存在IP
[root@typecodes ~]# telnet 10.10.223.123 9010
Trying 10.10.223.123...
# 然后打開另一個ssh會話,netstat服務器上tcp連接狀況
[root@typecodes ~]# netstat -anpt
tcp 0 1 10.169.218.97:53794 10.10.223.123:9010 SYN_SENT 4271/telnet
由下圖可知,telnet進程作為客戶端發送SYN包后,進入SYN_SENT狀態,等待服務端應答。

但是由於客戶端和目標IP的路由無法建立(也就是BZ遇到的情況),所以在3分鍾后該tcp鏈路顯示Connection timed out。

