小記TCP keepalive

前幾天在調查一個Kafka服務器tcp連接數過大的問題。具體情況是單臺Kafka的tcp連接數超過了3萬,都是ESTABLISHED狀態,到部分remote ip的連接數達到了幾百,且連接數每天還在持續增加。這批remote ip都是屬於同一個業務。
剛開始懷疑是Kafka某些條件下存在socket leakage的bug。但後來調查證實是防火牆引起的問題——Kafka服務器與這批業務服務器間存在一個防火牆,且配置了清理半小時的空閒連接。而我們使用的Kafka版本較低(0.8.2.1),在創建連接時沒有使用tcp keepalive。於是有些連接長時間沒有數據傳輸就被防火牆在中間悄悄幹掉了,而Kafka broker端沒有發現,殘留了大量無效連接。

其實Kafka官網已經記錄了這個issue(https://issues.apache.org/jira/browse/KAFKA-2096),解決方案就是在創建tcp連接時加上keepalive選項,在0.9.0版本中已經解決。我們的Kafka由於升級影響較大,爲降低風險採取了patch回當前版本的解決方案。

之前一直沒太深入瞭解過TCP Keepalive,藉此機會補一下課,也在此簡單記錄。


TCP keepalive選項,在創建tcp socket時默認是不打開的。默認的發送間隔較長,爲7200秒,即2小時。在linux內核中相關的配置參數爲

net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9

如果需要修改爲更短的keepalive間隔,可以用命令

# 修改爲20分鐘
sysctl -w net.ipv4.tcp_keepalive_time=1200

查看一個tcp連接是否使用了keepalive,可以用netstat -o查看,最後一列會是keepalive和倒計時。

要注意tcp keepalive是單向的,即只是單向的發送keepalive包且不需要response。


一個簡單例子。

server端:

nc -kl localhost 9000

client端,用python實現:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
print s.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE)

address = ('127.0.0.1', 9000)
s.connect(address)

連接狀態:

$ netstat -npo | grep 9000
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:48532             127.0.0.1:9000              ESTABLISHED 9780/python2.7      keepalive (7184.77/0/0)
tcp        0      0 127.0.0.1:9000              127.0.0.1:48532             ESTABLISHED 27441/nc            off (0.00/0/0)

從上面的最後一列可以看到,client到server的連接使用了keepalive,下次發送keepalive的倒計時爲7184秒。


參考資料:

  1. TCP Keepalive HOWTO
  2. linux下netstat --timers / -o詳解及keepalive相關
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章