前幾天在調(diào)查一個Kafka服務(wù)器tcp連接數(shù)過大的問題。具體情況是單臺Kafka的tcp連接數(shù)超過了3萬,都是ESTABLISHED狀態(tài),到部分remote ip的連接數(shù)達(dá)到了幾百,且連接數(shù)每天還在持續(xù)增加。這批remote ip都是屬于同一個業(yè)務(wù)。
剛開始懷疑是Kafka某些條件下存在socket leakage的bug。但后來調(diào)查證實是防火墻引起的問題——Kafka服務(wù)器與這批業(yè)務(wù)服務(wù)器間存在一個防火墻,且配置了清理半小時的空閑連接。而我們使用的Kafka版本較低(0.8.2.1),在創(chuàng)建連接時沒有使用tcp keepalive。于是有些連接長時間沒有數(shù)據(jù)傳輸就被防火墻在中間悄悄干掉了,而Kafka broker端沒有發(fā)現(xiàn),殘留了大量無效連接。
其實Kafka官網(wǎng)已經(jīng)記錄了這個issue(https://issues.apache.org/jira/browse/KAFKA-2096),解決方案就是在創(chuàng)建tcp連接時加上keepalive選項,在0.9.0版本中已經(jīng)解決。我們的Kafka由于升級影響較大,為降低風(fēng)險采取了patch回當(dāng)前版本的解決方案。
之前一直沒太深入了解過TCP Keepalive,借此機(jī)會補(bǔ)一下課,也在此簡單記錄。
TCP keepalive選項,在創(chuàng)建tcp socket時默認(rèn)是不打開的。默認(rèn)的發(fā)送間隔較長,為7200秒,即2小時。在linux內(nèi)核中相關(guān)的配置參數(shù)為
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是單向的,即只是單向的發(fā)送keepalive包且不需要response。
一個簡單例子。
server端:
nc -kl localhost 9000
client端,用python實現(xiàn):
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)
連接狀態(tài):
$ 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,下次發(fā)送keepalive的倒計時為7184秒。
參考資料: