以一道運維面試題開局:
你接觸過的單機最大并發數是多少?
操作系統最大文件打開數是65535,服務器又是怎么做到支持10w并發的?
你認為當前正常配置的服務器物理機最大并發數可以到多少?
說說你的理解與分析
作為一個運維人員拿到一臺服務器,需要完成哪些基線配置、內核優化參數你一定可以有理有據至少說出一二,但若要問題原理為何、憑什么并發與最大文件打開數有關又不完全有關,這是這篇文章給大家分享的。
如何表示一個TCP連接
四元組({localip, localport,remoteip,remoteport}): 在TCP連接中,最常用的標識方式是四元組,包括源IP地址、源端口號、目標IP地址和目標端口號。這四個值唯一地標識了一個TCP連接。這被稱為源IP地址、源端口號、目標IP地址和目標端口號的組合。
Socket句柄(五元組): 在編程中,TCP連接通常通過套接字(Socket)來表示和標識。一個Socket句柄通常由以下組成:協議(如TCP)、本地IP地址、本地端口號、遠程IP地址和遠程端口號。這些信息用于在編程中標識和操作TCP連接。
連接標識符: 在某些情況下,應用程序可能使用連接標識符或會話ID來唯一標識連接。這個標識符可以是應用程序自己定義的,用于跟蹤和管理連接。
TCP狀態: 除了上述的標識方式,TCP連接還可以通過其當前狀態來標識。TCP協議定義了多種狀態,如建立連接、數據傳輸、連接關閉等。連接的狀態可以用來識別連接的當前階段。
明確了四元組來標識一個tcp連接,那么這樣的tcp連接理論最大值應該取決于 源地址、源端口、目的地址、目的端口等四個維度的最大值來定義。
什么決定了服務器的最大并發數
如圖,理論最大并發數=服務器唯一五元組,端口、地址、協議都是有上限的,這個值不難算出。
作為客戶端他的最大連接數是多大
client每次發起tcp連接請求時,除非指定源端口,通常會隨機選出來一個空閑的本地端口(基本上你看到都是五位數以上),該端口是獨占不存在復用。因此操作系統端口數最多65536,0 又不能用,最多就剩下65535個,因此在純客戶端的情況下,不管連一樣的服務器地址和端口或者不一樣的服務器端口他最大值始終都受限制與客戶端本地的端口數量,然后一般1024以下又不讓你用 所以客戶端的最大連接數最多不會超過65000,理論就是2^16個.
關于這個客戶端的最大連接數還有一個場景是NAT客戶端的場景,同一個NAT網絡下,所有終端都是通過路由NAT了一個公網地址+端口進行訪問,這種情況下,所有終端的最大連接數合計應該是<65535的。
作為服務端的最大連接數是多大
那么對于服務器端來說,我們以TCP連接為例,我們假定單進程、單端口(注意這個前提,要考),單網卡。 這個條件下 四元組中兩項固定,那么服務器端的最大連接數理論值應該就是客戶端的IP地址數量客戶端的端口數量即2^32*2^16=2^48個。
但是,但是來了
每一條連接都是要消耗系統資源的,一個socket連接的建立需要消耗內存、需要建立socket,每個socket又都需要消耗一個文件描述符,文件描述符消耗的就是文件打開數,因此,如果你的最大文件打開數是1024,那么你的socket最多也就1024。
另外,一個socket是可以建立多個連接的,一個tcp連接標記為一個四元組,只要四個元素有一個不一樣那么就不是一個連接。
比如:
服務器ip是214.117.11.2,監聽80端口;
115.116.117.118 發來一條請求源端口為11111,這條tcp連接的四元組是(214.117.11.2,80,115.116.117.118,11111)
115.116.117.118 發來第二條請求,端口11112,新的四元組(214.117.11.2,80,115.116.117.118,11112).那么你的80端口就有了兩條連接;
若115.116.117.118 發來第三條請求,源端口還是11111,這個鏈接肯定無法建立,因為源端口不可復用,當然你換udp 是不是就可以了,因為udp 跟tcp 屬于不同協議,并不共享端口,協議不同,端口相同在操作系統中是允許存在。所以真正要標識一個鏈接是五元組,比四元組多了一個協議。
綜上所述,現實工作中,由于存在端口復用的情況,服務器同時支持tcp連接數跟端口的65535 沒有一一對應關系,而進程又跟文件描述符是一一對應關系,文件描述符消耗最大文件打開數,同時需要內存來維護。因此最大并發數受最大文件打開數、內存影響。
最大文件打開數在fs.file-max、nofile、/etc/systemd/system.conf中的關系
- file-max 系統最大打開文件描述符數,內核級,系統級,所有的限制都要在這個范圍之下
/proc/sys/fs/file-max中指定了系統范圍內所有金策會給你可打開的文件句柄的數量限制(系統級別、kernel-level)
The value in file-max denotes the maximum number of file handles that the linux kernel will allocate
臨時性設置:
echo 6553500 > /proc/sys/fs/file-max
永久:
fs.file-max=6553500
nr_open 單個進程可分配的最大文件數
為什么會提到這個參數,畢竟很少有人真的去改一個最大文件打開數超過1024*1024=1048576.更別說有人會去測試,比如最大文件打開數超過這個值,測試(慎用,建議測試環境使用,在你不知道這個腳本是什么功能的前提下,不要復制直接運行):
for i in `seq 100000 10000000`;do ulimit -n $i; [[ $? != 0 ]] && break;done
跟蹤原因就會發現,這個實際的ulimit值還受nr_open的限制。
這里貼下官網linux內核的解釋:
file-max:
The value in file-max denotes the maximum number of file-handles that the linux kernel will allocate. When you get lots of error messages about running out of file handles, you might want to increase this limit
nr_open
This denotes the maximum number of file-handles a process can allocate. Default value is 1024*1024 which should be enough for most machines. Actual limit depends on RLIMIT_NOFILE resource limit.
根據解釋,其實file-max是文件句柄,而非FD(文件描述符)。因此 file-max是內核可分配的最大文件數,nr_open是單個進程可分配的最大文件數。
- nofile 是用戶或進程級別的文件描述符限制,可以通過ulimit來修改。
臨時修改:
ulimit -n 65535
永久(要重啟服務器):
# /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
- /etc/systemd/system.conf 中的DefaultLimitNOFILE 是systemd級別的限制。
這里可能有運維同學會遇到這種問題:改了ulimit改了nofile 改了fs.file-max,但是使用yum安裝的服務,卻一直報文件打開數不足,服務器重啟就是不生效。原因是通過sytemd管理的服務,需要在這里也要修改最大文件打開數。當然比如MySQL 自身的mysqld.service中也可以來配置這個參數實現修改。
(為什么出現這樣的原因呢,主要看systemd服務本身是什么狀態,是系統實例還是用戶實例,通常都是系統實例,把--system換位--user 即可。)回到上面的問題,mysql是通過systemd管理的所以需要修改system.conf中的最大文件打開數,重啟服務即可。如果還有限制檢查
/usr/lib/systemd/system/mysqld.service
結論:
所有進程打開的文件描述符數量不能超過/proc/sys/fs/file-max
單個進程打開的文件描述符數不能超過user limit 中nofile soft limit
nofile 的soft limit 不能超過hard limit
nofile 的hard limit 不能超過 /proc/sys/fs/nr_open