1. 程式人生 > >tcp_timestamps tcp_tw_recycle引起的伺服器連線不上問題

tcp_timestamps tcp_tw_recycle引起的伺服器連線不上問題

最近一個非常奇怪的問題,多臺手機客戶端利用公司wifi連線一臺伺服器,但是頻繁出現連線不上情況,而且一臺能連線上,另一臺就會斷開。斷開的時候再嘗試連線,但是沒有apache跟tomcat的任何訪問記錄,但是3G連線不會出現這個問題。

最近發現一個PHP指令碼時常出現連不上伺服器的現象,除錯了一下,發現是TIME_WAIT狀態過多造成的,本文簡要介紹一下解決問題的過程。

遇到這類問題,我習慣於先用strace命令跟蹤了一下看看:

shell> strace php /path/to/file
EADDRNOTAVAIL (Cannot assign requested address)
從字面結果看似乎是網路資源相關問題。這裡順便介紹一點小技巧:在除錯的時候一般是從後往前看strace命令的結果,這樣更容易找到有價值的資訊。

檢視一下當前的網路連線情況,結果發現TIME_WAIT數非常大:

shell> netstat -nt | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
TIME_WAIT 28233
重複了幾次測試,結果每次出問題的時候,TIME_WAIT都等於28233,這真是一個魔法數字!實際原因很簡單,它取決於一個核心引數net.ipv4.ip_local_port_range:

shell> sysctl -a | grep port
net.ipv4.ip_local_port_range = 32768 61000
因為埠範圍是一個閉區間,所以實際可用的埠數量是:

shell> echo $((61000-32768+1))
28233
問題分析到這裡基本就清晰了,解決方向也明確了,內容所限,這裡就不說如何優化程式程式碼了,只是從系統方面來闡述如何解決問題,無非就是以下兩個方面:

首先是增加本地可用埠數量。這點可以用以下命令來實現:

shell> echo "net.ipv4.ip_local_port_range = 10240 61000" >> /etc/sysctl.conf
shell> sysctl -p
其次是減少TIME_WAIT連線狀態。網路上已經有不少相關的介紹,大多是建議:

shell> sysctl net.ipv4.tcp_tw_reuse=1
shell> sysctl net.ipv4.tcp_tw_recycle=1
注:通過sysctl命令修改核心引數,重啟後會還原,要想持久化可以參考前面的方法。

這兩個選項在降低TIME_WAIT數量方面可以說是立竿見影,不過如果你覺得問題已經完美搞定那就錯了,實際上這樣可能會引入一個更復雜的網路故障。

關於核心引數的詳細介紹,可以參考官方文件。我們這裡簡要說明一下tcp_tw_recycle引數。它用來快速回收TIME_WAIT連線,不過如果在NAT環境下會引發問題。

RFC1323中有如下一段描述:

An additional mechanism could be added to the TCP, a per-host cache of the last timestamp received from any connection. This value could then be used in the PAWS mechanism to reject old duplicate segments from earlier incarnations of the connection, if the timestamp clock can be guaranteed to have ticked at least once since the old connection was open. This would require that the TIME-WAIT delay plus the RTT together must be at least one tick of the sender’s timestamp clock. Such an extension is not part of the proposal of this RFC.

大概意思是說TCP有一種行為,可以快取每個連線最新的時間戳,後續請求中如果時間戳小於快取的時間戳,即視為無效,相應的資料包會被丟棄。

Linux是否啟用這種行為取決於tcp_timestamps和tcp_tw_recycle,因為tcp_timestamps預設就是開啟的,所以當tcp_tw_recycle被開啟後,實際上這種行為就被激活了。

現在很多公司都用LVS做負載均衡,通常是前面一臺LVS,後面多臺後端伺服器,以NAT方式構建,當請求到達LVS後,它修改地址資料後便轉發給 後端伺服器,但不會修改時間戳資料,對於後端伺服器來說,請求的源地址就是LVS的地址,加上埠會複用,所以從後端伺服器的角度看,原本不同客戶端的請 求經過LVS的轉發,就可能會被認為是同一個連線,加之不同客戶端的時間可能不一致,所以就會出現時間戳錯亂的現象,於是後面的資料包就被丟棄了,具體的 表現通常是是客戶端明明發送的SYN,但服務端就是不響應ACK,還可以通過下面命令來確認資料包不斷被丟棄的現象:

shell> netstat -s | grep timestamp
... packets rejects in established connections because of timestamp
如果伺服器身處NAT環境,安全起見,通常要禁止tcp_tw_recycle,至於TIME_WAIT連線過多的問題,可以通過啟用tcp_tw_reuse來緩解。

進一步思考,既然必須同時啟用tcp_timestamps和tcp_tw_recycle才會觸發這種現象,那隻要禁止 tcp_timestamps,同時啟用tcp_tw_recycle,就可以既避免NAT丟包問題,又降低TIME_WAIT連線數量。如果伺服器並不 依賴於RFC1323,那麼這種方法應該也是可行的,不過最好多做測試,以防有其他的副作用。

shell> sysctl net.ipv4.tcp_timestamps=0
shell> sysctl net.ipv4.tcp_tw_recycle=1

總體來說,這次網路故障本身並沒什麼高深之處,本不想羅羅嗦嗦寫這麼多,不過拔出蘿蔔帶出泥,在過程中牽扯的方方面面還是值得大家品味的,於是便有了這篇文字。

轉自

http://huoding.com/2012/01/19/142

 

類似的:

tcp 核心引數對NAT 使用者的影響

tcp_tw_recycle和tcp_timestamps導致connect失敗問題

底層通訊協議問題排查案例

 

update 20120913

我遇到的問題,看過上邊的四篇文章後我們就知道問題的原因了。

故障描述:

client (windows/linux) < -- > linux nat 伺服器 < -- > web server(linux)

這裡500 多臺client 同過linux nat 伺服器上網,一直執行正常,直到最近上有同事反應,linux client 使用者無法訪問某網站頻道,而windows client使用者則沒有問題,真像上面說的一樣人民問題嗎?

 

故障解決:

echo 0 > /proc/sys/net/ipv4/tcp_tw_recycle
sysctl -p
//tcp_tw_recycle預設是關閉的,有不少伺服器,為了提高效能,開啟了該選項

 

故障分析:

linux client 使用者無法訪問某網站頻道,而其他網站這正常!問題在web server 上!

抓包
windows(xp)

tcpdump -vvn dst 211.x.x.x 
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
10:49:05.600205 IP (tos 0x0, ttl 64, id 4280, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.4.157.1244 > 211.x.x.x.80: Flags [S], cksum 0x9f8d (correct), seq 4210943732, win 64240, options [mss 1460,nop,wscale 0,nop,nop,sackOK], length 0
10:49:05.607071 IP (tos 0x0, ttl 64, id 4281, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.4.157.1244 > 211.x.x.x.80: Flags [.], cksum 0x2c80 (correct), seq 4210943733, ack 2368349854, win 64240, length 0
10:49:55.618477 IP (tos 0x0, ttl 64, id 4337, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.4.157.1244 > 211.x.x.x.80: Flags [.], cksum 0x2c7f (correct), seq 0, ack 214, win 64028, length 0
10:49:55.637271 IP (tos 0x0, ttl 64, id 4338, offset 0, flags [DF], proto TCP (6), length 40)
    192.168.4.157.1244 > 211.x.x.x.80: Flags [F.], cksum 0x2c7e (correct), seq 0, ack 214, win 64028, length 0

linux(ub12.04)

tcpdump -i eth1 -vvn host 192.168.4.35 and  211.x.x.x 
tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
11:57:08.197353 IP (tos 0x10, ttl  62, id 8687, offset 0, flags [DF], proto: TCP (6), length: 60)192.168.4.35.57699 > 211.x.x.x.http: S, cksum 0x78d4 (correct), 50589925:50589925(0) win 14600 <mss 1460,sackOK,timestamp 41668874 0,nop,wscale 7>
11:57:09.193852 IP (tos 0x10, ttl  62, id 8688, offset 0, flags [DF], proto: TCP (6), length: 60)192.168.4.35.57699 > 211.x.x.x.http: S, cksum 0x77da (correct), 50589925:50589925(0) win 14600 <mss 1460,sackOK,timestamp 41669124 0,nop,wscale 7>
11:57:11.197844 IP (tos 0x10, ttl  62, id 8689, offset 0, flags [DF], proto: TCP (6), length: 60)192.168.4.35.57699 > 211.x.x.x.http: S, cksum 0x75e5 (correct), 50589925:50589925(0) win 14600 <mss 1460,sackOK,timestamp 41669625 0,nop,wscale 7>
.....略
 

結論:windows 發出的tcp 包中沒有timestamp,固能夠完成tcp 的三次握手,而linux 發出的tcp 包中包含timestamp,webserver 拒絕傳送syn/ack包,你能看到包的標誌全部是S,所以不能完成tcp 的三次握手,也就不能訪問web server 了。

 

這裡簡單解釋下tcpdump 資料包意義

TCP包的輸出資訊
用TCPDUMP捕獲的TCP包的一般輸出資訊是:
src > dst: flags data-seqno ack window urgent options
src > dst:表明從源地址到目的地址, flags是TCP包中的標誌資訊,S 是SYN標誌, F (FIN), P (PUSH) , R (RST) "." (沒有標記); 
data-seqno是資料包中的資料的順序號, ack是下次期望的順序號, window是接收快取的視窗大小, 
urgent表明資料包中是否有緊急指標. Options是選項.

每一行中間都有這個包所攜帶的標誌:
S=SYN,發起連線標誌
P=PUSH,傳送資料標誌
F=FIN,關閉連線標誌
ack    表示確認包
RST= RESET,異常關閉連線
. 表示沒有任何標誌
--------------------- 
作者:江湖邗孜 
來源:CSDN 
原文:https://blog.csdn.net/fanyun7654/article/details/20725783?utm_source=copy 
版權宣告:本文為博主原創文章,轉載請附上博文連結!