詳解linux中的backlog


什麼是backlog

backlog是linux下socket函數之listen的參數,當應用程序調用listen系統調用讓一個socket進入LISTEN狀態時,須要指定一個backlog參數。這個參數常常被描述爲,新鏈接隊列的長度限制。html

因爲TCP創建鏈接須要進行3次握手,一個新鏈接在到達ESTABLISHED狀態能夠被accept系統調用返回給應用程序前,必須通過一箇中間狀態SYN RECEIVED。這意味着,TCP/IP協議棧在實現backlog隊列時,有兩種不一樣的選擇:linux

  • 僅使用一個隊列,隊列規模由listen系統調用backlog參數指定。當協議棧收到一個SYN包時,響應SYN/ACK包而且將鏈接加進該隊列。當相應的ACK響應包收到後,鏈接變爲ESTABLISHED狀態,能夠嚮應用程序返回。這意味着隊列裏的鏈接能夠有兩種不一樣的狀態:SEND RECEIVED和ESTABLISHED。只有後一種鏈接才能被accept系統調用返回給應用程序。bootstrap

  • 使用兩個隊列——SYN隊列(待完成鏈接隊列)和accept隊列(已完成鏈接隊列)。狀態爲SYN RECEIVED的鏈接進入SYN隊列,後續當狀態變動爲ESTABLISHED時移到accept隊列(即收到3次握手中最後一個ACK包)。顧名思義,accept系統調用就只是簡單地從accept隊列消費新鏈接。在這種狀況下,listen系統調用backlog參數決定accept隊列的最大規模。服務器

對於linux操做系統,內核在2.2以後的版本,tcp/ip協議實現了第二種方案,即一個syn隊列,一個accept隊列,syn隊列的長度由系統級別設置,accept隊列的長度能夠由應用級別設置,tcp建連交互流程以下圖所示:網絡

  1. client 端使用 connect() 向 server 端發起鏈接請求(發送 syn 包),此時 client 端的 TCP 的狀態爲 SYN_SENT。併發

  2. server 端在收到 syn 包後,將 TCP 相關信息放到 syn queue(半鏈接隊列)中,同時向 client 發送 syn+ack,server 端 TCP 的狀態爲 SYN_RCVD。socket

  3. client 端收到 server 端的 syn+ack 後,向 server 端發送 ack,此時 client 端的 TCP 的狀態爲 ESTABLISHED。Server 端收到 ack 確認後,從 syn queue 裏將 TCP 信息取出,並放到 accept queue(全鏈接隊列)中,此時 server 端的 TCP 的狀態爲 ESTABLISHED。tcp

通過以上三個過程,client 端和 server 端創建了鏈接,即所謂三次握手的過程。
由此,咱們已經知道了什麼是backlog,以及半鏈接隊列、全鏈接隊列的由來。下文當中提到的backlog均表示全鏈接隊列長度。那怎麼設置backlog的值呢?


如何設置backlog

backlog的大小有兩方面因素決定,一是系統層面,二是應用層面
  1. 系統層面:somaxconn參數,能夠經過編輯/proc/sys/net/core/somaxconn的值進行設置函數

  2. 應用層面:對於netty服務端來講,經過serverbootstrap的option進行設置,即option((ChannelOption.SO_BACKLOG,number),number即爲要設置的大小,類型爲int高併發

backlog最終的取值爲兩者中的最小值,即min(backlog,somaxconn),在服務啓動以後,咱們能夠經過,ss -tnlp進行查看,以下圖所示:

在listen狀態狀況下:Recv-Q表示存在於backlog當中未被服務端應用程序accept的隊列大小;Send-Q表示最大的backlog的大小,即咱們設置的backlog的大小min(backlog,somaxconn)。那當設置backlog時應參考怎麼的設置標準呢?

backlog設置標準

在設置backlog時,既不能太大,也不能過小,設置太大,當訪問流量忽然增長超過服務器的負載時,客戶端不能快速失敗,形成讀取鏈接超時,對服務端來說,會影響網絡I/O,同時形成內存使用過大,cpu負載增長;若是設置過小,不能充分發揮服務端的負載能力,而且會客戶端形成鏈接失敗的狀況。應根服務端可以承受的最大qps進行設置,backlog的大小,應設置成在服務端最大可以承受qps的1-1.5倍左右。


backlog使用分析

對於backlog隊列的使用狀況,咱們能夠經過netstat進行查詢,以下圖所示:

如圖所示,咱們能夠看到當前backlog已使用的隊列長度爲1。由以上分析咱們能夠知道,處於backlog隊列中的鏈接,還未被應用線程accept。也就是說咱們能夠經過netstat查詢到當前鏈接,但該鏈接並未關聯進程pid。分析圖中全部鏈接,咱們能夠知道,端口號爲34328未關聯pid,表明此鏈接處於全鏈接隊列當中。
固然咱們也能夠經過ss命令進行查詢當前監聽端口號的backlog使用狀況,以下圖所示:

如圖所示,當前監聽端口全鏈接隊列長度爲2,當全鏈接隊列的使用長度爲(backlog+1)時,表明次全鏈接隊列已滿,那當全鏈接隊列已滿的狀況下,當又有新建鏈接請求進來時會怎麼處理呢?
當再有新的創建鏈接的請求是,會根據服務端的/proc/sys/net/ipv4/tcp_abort_on_overflow設置而進行不一樣的處理。
  • 當tcp_abort_on_overflow=0,直接丟棄該ACK,經過tcpdump抓包,能夠看到以下的交互流程

此時服務端處於 【syn_rcvd】的狀態,客戶端處於 【established】的狀態:

  • 當tcp_abort_on_overflow=1,發送RST通知client,client會報connection reset by peer

抓包查看其流程以下所示:

當全鏈接隊列溢出時,有哪些指標能夠說明呢,咱們又有那可有效的查詢手段呢?咱們能夠經過netstat -s命令進行查詢:

如圖所示,4879 times表示全鏈接隊列溢出的次數,隔幾秒查詢一次,若是這個數字一直在遞增,說明全鏈接隊列出現了溢出的狀態。
咱們又怎麼肯定究竟是哪一個監聽端口出現了全鏈接隊列溢出的狀況呢?
  1. 經過ss -tnlp 查詢監聽端口全鏈接隊列的使用狀況。

  2. 經過netstat查詢出鏈接狀態處於established、但未關聯進程號的鏈接,此鏈接對應着源端口以及目標端口,此鏈接的源端口就是咱們應用程序監聽的端口,即出現backlog隊列溢出的端口號。

總之,對於咱們平常工做當中,不管是長鏈接,短鏈接,以及各類的消息中間件,只要牽涉到監聽端口,都會牽涉到backlog,當咱們瞭解backlog以後,對咱們處理網絡問題,已經如何提交系統的高併發將大有裨益。


參考文獻

http://veithen.io/2014/01/01/how-tcp-backlog-works-in-linux.html
https://www.jianshu.com/p/7fde92785056

做者簡介

李雙全,民生科技有限公司用戶體驗技術部Firefly移動金融開發平臺Java開發工程師。

相關文章
相關標籤/搜索