redis connect timeout問題排查

賀子_DBA時代發表於2021-09-04

問題表現:週期性出現connect timeout: 具體如下:

redis.clients.jedis.exceptions.JedisConnectionException

java.net.SocketException

java.net.SocketTimeoutException: connect timed out

問題定位:redis 引數tcp-backlog預設設定過小(還有別的可能會造成上面的問題,但是我這次是因為這個)

這個場景下,如果我們到伺服器上看看 listen情況,watch "netstat -s | grep listen",會看到“xxx times the listen queue of a socket overflowed”,並且這個xxx在不斷增加,這個xxx就是我們沒有對網路請求正常處理的次數

問題分析:

首先說下TCP中backlog

Linux核心為每個TCP伺服器程式維護兩條backlog佇列,一條是TCP層的未連線佇列,一條是應用層的已連線佇列,分別對應net.ipv4.tcp_max_syn_backlog和net.core.somaxconn兩個核心引數。

一個客戶端連線在完成TCP 3次握手之前首先進入到未連線佇列,完成握手之後正式建立連線,進入已連線佇列,交付給應用程式處理。應用程式呼叫accept()函式從已連線佇列取出連線進行處理。應用層在呼叫listen()函式時指定的backlog是已連線佇列大小,如果大於somaxconn將被設為somaxconn。

解釋:針對redis server來說已經完成三次握手的tcp連線進入accept 佇列,然後等待redis server去從accept 佇列中取出來和redis server建立連線,然後accept 佇列就會減少一個值。這裡說的accept 佇列是由net.core.somaxconn和redis引數tcp-backlog的最小值來控制!

核心問題:如果應用層不呼叫accept()函式處理一個連線,或者處理不及時的話,將會導致已連線佇列堆滿。已連線佇列已滿的話會導致未連線佇列在處理完3次握手之後無法進入已連線佇列,最終也導致未連線佇列堆滿,在伺服器看到處於未連線佇列中的連線狀態為SYN_RECV。 新進來的客戶端連線將會一直處於SYN_SENT狀態等待伺服器的ACK應答,最終導致連線超時。

檢視佇列大小:

檢視未連線佇列值:

[root@t1-26-89 redis-3.0.7]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog

262144

檢視已連線佇列值:

[root@t1-26-89 redis-3.0.7]# cat /proc/sys/net/core/somaxconn

32768

修改佇列大小:可以直接改寫這兩個檔案的值。要永久修改這兩個核心引數的話可以寫到/etc/sysctl.conf:

改完後執行sysctl -p 讓修改立即生效。

vim /etc/sysctl.conf

net.ipv4.tcp_max_sync_backlog=1024

net.core.somaxconn = 2048

sysctl -p

再次解釋下tcp三次握手:

我們看到Send-Q的值為100, 即是我們配置的tcp-backlog值. 為了搞清楚這個值的意思, 瞭解了下tcp的三次握手進行中的一些queue的知識. 參考下圖我們可以看到在server接收到sny的時候會進入到一個syn queue佇列, 當server端最終收到ack時轉換到accept queue佇列. 然後透過ss或者netstat就可以看到listen狀態下的連線, 其Send-Q就是這個accept queue佇列的最大值. 只有server端執行了accept後才會從這個佇列中移除這個連線. 這個值的大小是受somaxconn影響的, 因為是取的它們兩者的最小值, 所以如果要調大的話必需修改核心的somaxconn值.

關於redis 的引數tcp-backlog:

預設值 511, 如果tcp-backlog大於/proc/sys/net/core/somaxconn值,則截斷為/proc/sys/net/core/somaxconn值

tcp-backlog:511

此引數確定了TCP連線中已完成佇列(完成三次握手之後)的長度, 當然此值必須不超過Linux系統定義的/proc/sys/net/core/somaxconn值(預設引數值是128),tcp-backlog引數預設是511。當系統併發量大並且客戶端速度緩慢的時候,可以將這二個引數一起參考設定。


建議修改為 2048

修改somaxconn

該核心引數預設值一般是128,對於負載很大的服務程式來說大大的不夠。一般會將它修改為2048或者更大。

echo 2048 > /proc/sys/net/core/somaxconn 但是這樣系統重啟後儲存不了

在/etc/sysctl.conf中新增如下

net.core.somaxconn = 2048

然後在終端中執行

sysctl -p

總結:redis的每個引數都值得研究,做出合適的調整,針對redis server來說已經完成三次握手的tcp連線進入accept 佇列,然後等待redis server去從accept 佇列中取出來和redis server建立連線,然後accept 佇列就會減少一個值。這裡說的accept 佇列是由net.core.somaxconn和redis引數tcp-backlog的最小值來控制!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29654823/viewspace-2790380/,如需轉載,請註明出處,否則將追究法律責任。

相關文章