騰訊三面:一臺服務器,最大支持的TCP連接數是多少?

2024年2月6日 21点热度 0人点赞

最近有讀者面試騰訊的時候,被問到 2 個很有意思的問題:

  • 一個服務端進程最大能支持多少條 TCP 連接?
  • 一臺服務器最大能支持多少條 TCP 連接?

很多同學第一反應就是端口的限制,端口號最多是 65536個,那就最多隻能支持 65536 條 TCP 連接。

實際上這是不對的!

今天都帶大傢分析一波這兩個問題。

一個服務端進程最多能支持多少條 TCP 連接?

首先我們要知道 TCP 連接本質上在內核裡就是一個 socket 對象。

struct socket {  
    ....
    //INET域專用的一個socket表示, 提供了INET域專有的一些屬性,比如 IP地址,端口等
    struct sock             *sk;  
    //TCP連接的狀態:SYN_SENT、SYN_RECV、ESTABLISHED.....
    short                   type;  
    ....
};  
struct inet_sock {  
...
  __u32    daddr;   //IPv4的目標地址。  
  __u16    dport;   //目標端口。   
  __u32    saddr;   //源地址。  
  __u16    sport;   //源端口。  
...
};  

這個 socket 對象也就是一個數據結構,裡面包含了 TCP 四元組的信息:源IP、源端口、目標IP、目標端口。

TCP 四元組

所以, 隻要確認了【源IP、源端口、目標IP、目標端口】這四個信息,就能在內核中找到這個 socket 對象,也就能確定一條 TCP 連接。

一個服務端進程通常是監聽 1 個端口號(當然也可能監聽多個端口號,這裡不考慮),比如我的圖解網站的 nginx 服務,就監聽了 443 端口。

你們看圖解網站的時候,實際上就是通過 nginx 服務把網頁數據發送給你們的。

然後,服務端進程除了會固定監聽某個一個端口之外,也通常會綁定 0.0.0.0 IP 地址。

這個IP地址是特殊的, 0.0.0.0 指的是本機上的所有IPV4地址,如果一個主機有兩個 IP 地址,192.168.1.110.1.2.1,並且該主機上的一個服務監聽的地址是0.0.0.0,那麼通過兩個 IP 地址都能夠訪問該服務。

所以一個服務端進程,意味著他的 IP地址和端口號是固定的(0.0.0.0:443)。

也就是當客戶端與服務端建立一條 TCP 連接的時候,這個 TCP 連接的四元組信息中服務端的 IP地址和端口號是固定的,能產生變化的就是客戶端的 IP 地址和端口號了。

因此,一個服務端進程最大能支持的 TCP 連接個數的計算公式如下:

對 IPv4,客戶端的 IP 數最多為 232 次方,客戶端的端口數最多為 216 次方。

那麼一個服務端進程理想情況下,最大的 TCP 連接數約為 248 次方(2^32 (ip數) * 2^16 (端口數),這數值是非常誇張的了,約等於兩百多萬億!

當然,服務端進程最大能支持的 TCP 連接數遠不能達到理論上限,還會受到文件描述符、內存大小資源的限制,畢竟 socket 在 Linux 的視角其實就是文件資源,而且一個 socket 對象也會占用一定的內存資源。

因此,會受以下因素影響:

  • 文件描述符限制,每個 TCP 連接都是一個文件,如果文件描述符被占滿了,會發生 Too many open files。Linux 對可打開的文件描述符的數量分別作了三個方面的限制:
    • 系統級:當前系統可打開的最大數量,通過 cat /proc/sys/fs/file-max 查看;
    • 用戶級:指定用戶可打開的最大數量,通過 cat /etc/security/limits.conf 查看;
    • 進程級:單個進程可打開的最大數量,通過 cat /proc/sys/fs/nr_open 查看;
  • 內存限制,每個 TCP 連接都要占用一定內存,操作系統的內存是有限的,如果內存資源被占滿後,會發生 OOM。

一臺服務器最大最多能支持多少條 TCP 連接?

前面分析是一個服務端進程理的情況,理論上能最大支持約為 248 次方(2^32 (ip數) * 2^16 (端口數),約等於兩百多萬億!

那到了一臺服務器的視角就會有一點不一樣。

一臺服務器是可以有多個服務端進程的,每個服務端進程監聽不同的端口,比如:ssh的22,Redis的6339,當然所有65535個端口你都可以用來監聽一遍。

當然所有65535個端口你都可以用來監聽一遍,這樣理論上線就到了2的32次方(ip數)×2的16次方(port數)×2的16次方(服務器port數)個,感興趣你可以算一下,這個基本相當於無窮個了。

不過理想和實際總是會有差距的!

因為Linux每維護一條TCP連接都要花費資源,處理連接請求,保活,數據的收發時需要消耗一些CPU,維持TCP連接主要消耗內存。

我們題目的問題是考慮最大多少個連接,所以我們先不考慮數據的收發,那麼TCP在靜止的狀態下,就不怎麼消耗CPU了,主要消耗內存,而Linux上內存是有限的。

首先,我們要知道一條處於 ESTABLISH 狀態的 TCP 連接具體占用多大內存?

一個 TCP 對象占用的大小,等於它所包含的一些數據結構占用大小的總和,也是就把上面這些數據結構的大小累加起來,就是一個 TCP 連接占用的大小了。

這裡直接給大傢一個結論,一條處於 ESTABLISH 狀態的 TCP 連接占用的大小是 3.44 KB(0.81K 2.19K 0.19K 0.25K)。

TCP對象內存開銷總結

也就是,每一條靜止狀態的TCP連接大約需要吃 3.44K 的內存。

那麼 8 GB 物理內存的服務器,最大能支持的 TCP 連接數=8GB/3.44KB=2,438,956(約240萬)

當然, 實際過程中的 TCP 連接,肯定不是靜止狀態的,還會進行發送數據和接收數據了,那麼這些過程還是會額外消耗更多的內存資源的,並發很難達到百萬級別。

總結

一個服務端進程最多能支持多少條 TCP 連接?

如果在不考慮服務器的內存和文件句柄資源的情況下,理論上一個服務端進程最多能支持約為 248 次方(2^32 (ip數) * 2^16 (端口數),約等於兩百多萬億!

但是在實際中是支持不了這個數值的,每個 TCP 連接都是一個文件,會占用文件句柄資源,也會占用一定的內存空間。

一臺服務器最大最多能支持多少條 TCP 連接?

一臺服務器是可以有多個服務端進程的,每個服務端進程監聽不同的端口,當然所有65535個端口你都可以用來監聽一遍。

當然所有65535個端口你都可以用來監聽一遍,這樣理論上線就到了2的32次方(ip數)×2的16次方(port數)×2的16次方(服務器port數)個,這個基本相當於無窮個了。

但是 Linux每維護一條TCP連接都要花費內存資源的,每一條靜止狀態(不發送數據和不接收數據)的 TCP 連接大約需要吃 3.44K 的內存,那麼 8 GB 物理內存的服務器,最大能支持的 TCP 連接數=8GB/3.44KB=2,438,956(約240萬)。

實際過程中的 TCP 連接,還會進行發送數據和接收數據了,那麼這些過程還是會額外消耗更多的內存資源的,並發很難達到百萬級別。

完!你學廢了嗎?理想和現實的差距!