從一個線上服務器警告談談backlog


緣起

雙十一如期而至,此時的我因為在處理客戶的一個問題已經陷入了忙碌。突然,不斷接到駐場實施發來的反饋,都是相同的反饋——"客戶端操作緩慢"。

我現在負責的服務器是一台接口服務器,所有的賣家都要通過這台服務器連接到自己的數據庫上,不得小覷。於是我立馬放下手頭的話,打開了我事先安裝好的服務器監控軟件(netdata),便看到了下面的警告:

其實,作為一個服務器端新手,我並不知道什么意思。但是客戶反饋慢啊!屁股一想也知道是因為服務器資源不夠了嘛!於是,我立馬把一小部分客戶的配置切換到了另外一台准備好的服務器上,危機化解了,我擦了一把汗。

所以,什么是tcp accept queue?

通過搜索這個警告,找到了一個叫backlog的關鍵字,網上很多辦法是增大這個值,因為當時雙十一已經過去了,客戶已經沒有那么多單子發貨了。所以,也沒有驗證這個方法是否對不對。但backlog的迷還一直留在我的腦海里。

http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html

直到有一天,我遇到了上面的一篇文章,才對backlog有較為具體的認識。我簡單的根據文中的意思介紹下backlog參數。

要搞清楚這個問題,我們必須要先了解下tcp建立鏈接的三次握手,從圖中可以看出,第一次握手是客戶端給服務器發送syn,此時服務器進入SYN_RECV狀態(也叫未完成鏈接,對應圖中的SYN_RCVD),那么既然是一個服務器,所以接受這樣的鏈接就不是一個,是很多,所以會有一個隊列去記錄這樣的未完成鏈接,暫且稱為syn隊列。

第一次握手完成后,服務器會給客戶端發送 SYN+ACK,客戶端收到后,這個鏈接在客戶端方面已經是ESTABLISHED狀態了。客戶端接着會給服務器發送ACK,當服務器收到這個ACK的時候,服務器上的該鏈接由SYN_RECV 狀態到ESTABLISHED狀態,此時它進入了一個新隊列,這里叫它accept隊列。顧明思議就是供accept來消耗的請求。

兩個隊列的長度限制由什么參數決定?

經過上面分析,我們知道服務器端處理的請求鏈接是有兩個隊列來控制,syn隊列(對應圖中的syns queue)是等待客戶端回復ack的鏈接,叫做未完成鏈接或者半連接,而完成三次握手后,該鏈接進入accept隊列,那么這兩個隊列的長度是多少呢?

由上圖可以看出,syn隊列就是由 /proc/sys/net/ipv4/tcp_max_syn_backlog參數來控制。處於這個隊列上的鏈接需要等待的全雙工的通信時間,所以如果這個參數開的太小,可能對於一些並發性比較高的http服務器來說,容易成為瓶頸。因為這個syns隊列滿了以后,新的請求就因為隊列滿的緣故而被忽略。

而accept函數是有兩個參數共同決定的,分別是系統函數listen函數的backlog參數以及內核參數somaxconn,這兩者會取最小值,如果應用服務器能夠很快的從accept隊列中取出任務並執行,這個隊列並不會成為負擔,但如果每個請求都要耗費很長時間處理的話,那么服務器處理線程將會被打滿,這個時候accept隊列就會被占滿。

accept隊列被占滿的時候,會引入一個新的問題,那就是此時如果未完成鏈接接收到客戶端的ack完成三次握手,需要轉入accept隊列,此時該怎么辦?此時,如果
tcp_abort_on_overflow參數為0的話,此時服務器什么也不做,忽略掉這次申請轉到accept隊列的請求。那么這個請求就仍然留在了syns隊列,服務器隔斷時間會再次發送syn+ack給客戶端,也就是說會重復走第二部握手。所以,這里accept隊列也應該設置大一些,這樣可以省去服務器重復發請求的消耗。

關於syn隊列長度的限制,大部分文獻說的是由 tcp_max_syn_backlog 參數控制,但也有部分博客有更詳細的說明,認為syn隊列不僅僅受控制與該參數,帶着這個疑問,我也看了些源碼:

以上linux源碼中,max_qlen_log中代表syn隊列的最大長度,sysctl_max_syn_backlog 即是tcp_max_syn_backlog參數的值。第一行的nr_table_entries 是計算這個最大值的因子,它是作為一個參數傳入進來的,它是傳入的backlog參數,上面已經介紹過backlog參數是內核參數somaxconn和listen函數中傳入的backlog的最小值,而通過第一行源碼我們可以看出來,這里syn隊列的最大值是取backlog 和 tcp_max_syn_backlog的最小值,因此得到以下結論。

syn queue length = min(somaxconn,backlog,tcp_max_syn_backlog)

accept queue length = min(somaxconn,backlog)

實驗驗證

口說無憑,下面進行一個實驗,來按照上面的理論分析下。

准備

OS 雲機 1核1G的入門機

服務器是nginx + php-fpm

服務器監控軟件netdata

ab壓測工具

step 1 : 參數設置

經過上面對兩個隊列的長度限制分析知道somaxconn參數對兩個隊列有影響,於是設置somaxconn的值為8,其他backlog參數均為1024。

net.ipv4.tcp_max_syn_backlog = 1024

net.core.somaxconn = 8

net.ipv4.tcp_syncookies = 0

為了避免其他因素的影響我們的測試php文件中只有一句

ehco hello world;

step2: 開始壓測

ab 壓測 工具並發512個請求,請求總數5120個,請求發送完,發現netdata已經收到了報錯信息如下:

step3:得到結果

此時,可以看到兩個隊列都已經開始drop鏈接了。

step4:將somaxconn參數為1024,再次相同條件壓測發現已經沒有警告提示了

結論

通過合理的參數設置,是可以提高服務器報警的門檻,將參數調整到服務器能夠承受的最大值,確實可以在一定程度上提高服務器的容量。一方面,隊列增大后,可以暫時容納這些隊列,供應用程序消耗。如果隊列設置過小,那么新來的鏈接就會被忽略,客戶端還會進行重試,另一方面如果accept很快被充滿的話,syn隊列會被阻塞,而且syn隊列上的鏈接仍要去消耗資源對客戶端重新發送syn+ack,導致一個惡性循環。

但這不意味着盡量調整參數就可以萬事大吉了,在一些突發訪問的網絡中,調大參數可以緩解網絡情況,但此時客戶端訪問服務器肯定不會很快,因為服務器已經陷入了繁忙狀態。如果服務器一直處於報警狀態,即鏈接不是突發性的,而是長時間處於一個比較高的數量,那么就需要進行優化了。優化分為兩方面,第一方面應用程序的優化,即讓每個鏈接的處理速度加快,這樣線程可以快速的釋放,accept消耗加快,服務器處理速度加快。另外一方面,就是擴容服務器,通過負載均衡水平擴展計算資源,讓鏈接分配到不同機器上快速消耗。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM