單機服務器支持千萬級併發長連接的壓力測試

應用場景
聊天室或即時消息推送系統等,因爲很多消息需要到產生時才推送給客戶端,所以當沒有消息產生時,就需要hold住客戶端的連接,這樣,當有大量的客戶端時,要hold住大量的長連接。

 

服務器配置
此處我們按照10M併發連接爲目標進行配置。

一般服務器默認限制1024個文件句柄,也就是最多支持1024個併發長連接,在root用戶下編輯/etc/security/limits.conf文件,修改:


* soft nofile 1048576
* hard nofile 1048576


·soft是一個警告值,而hard則是一個真正意義的閾值,超過就會報錯。

·soft 指的是當前系統生效的設置值。hard 表明系統中所能設定的最大值

·nofile – 單進程打開文件的最大數目

·星號表示針對所有用戶,若僅針對某個用戶登錄ID,請替換星號

這樣理論上10個進程可以達到10m個併發長連接,但是在測試時會發現,併發數最多隻能到達28200左右,此時,需要修改默認的本地端口範圍。

 

linux系統端口範圍爲0-65536,系統提供了默認的端口範圍:

cat /proc/sys/net/ipv4/ip_local_port_range 
32768 61000


故當前端口使用範圍爲61000-32768約爲28200個,將端口範圍擴大,修改/etc/sysctl.conf,增加一行:

net.ipv4.ip_local_port_range= 1024 65535

保存,使之生效


sysctl –p

接着在測試端程序發出的連接數量大於某個值(大概爲40萬時)是,通過dmesg命令查看會得到大量警告信息:[warn]socket: Too many openfiles in system

此時需要修改file-max,表示系統所有進程最多允許同時打開所有的文件句柄數,系統級硬限制。添加fs.file-max = 1048576到/etc/sysctl.conf中,sysctl -p保存並使之生效。

 

在服務端連接達到一定數量時,通過查看dmesg命令查看,發現大量TCP: toomany of orphaned sockets錯誤,此時需要調整tcp socket參數,添加:

net.ipv4.tcp_mem= 786432 2097152 3145728
net.ipv4.tcp_rmem= 4096 4096 16777216
net.ipv4.tcp_wmem= 4096 4096 16777216
net.ipv4.tcp_max_orphans= 131072 

net.ipv4.tcp_rmem用來配置讀緩衝的大小,三個值,第一個是這個讀緩衝的最小值,第三個是最大值,中間的是默認值。我們可以在程序中修改讀緩衝的大小,但是不能超過最小與最大。爲了使每個socket所使用的內存數最小,我這裏設置默認值爲4096。

net.ipv4.tcp_wmem用來配置寫緩衝的大小。

讀緩衝與寫緩衝的大小,直接影響到socket在內核中內存的佔用。

而net.ipv4.tcp_mem則是配置tcp的內存大小,其單位是頁,1頁等於4096字節。三個值分別爲low, pressure, high

·low:當TCP使用了低於該值的內存頁面數時,TCP不會考慮釋放內存。

·pressure:當TCP使用了超過該值的內存頁面數量時,TCP試圖穩定其內存使用,進入pressure模式,當內存消耗低於low值時則退出pressure狀態。

·high:允許所有tcp sockets用於排隊緩衝數據報的頁面量,當內存佔用超過此值,系統拒絕分配socket,後臺日誌輸出“TCP: too many of orphaned sockets”。

 

一般情況下這些值是在系統啓動時根據系統內存數量計算得到的。 根據當前tcp_mem最大內存頁面數是1864896,當內存爲(1864896*4)/1024K=7284.75M時,系統將無法爲新的socket連接分配內存,即TCP連接將被拒絕。實際測試環境中,據觀察大概在99萬個連接左右的時候(零頭不算),進程被殺死,觸發outof socket memory錯誤(dmesg命令查看獲得)。每一個連接大致佔用7.5K內存(下面給出計算方式),大致可算的此時內存佔用情況(990000 * 7.5/ 1024K = 7251M)。這樣和tcp_mem最大頁面值數量比較吻合,因此此值也需要修改。

另外net.ipv4.tcp_max_orphans這個值也要設置一下,這個值表示系統所能處理不屬於任何進程的 socket數量,當我們需要快速建立大量連接時,就需要關注下這個值了。當不屬於任何進程的socket的數量大於這個值時,dmesg就會看 到”too many of orphaned sockets”。

 

綜上,服務端需要配置的內容做個彙總:

/etc/sysctl.conf 添加:

fs.file-max= 10485760
net.ipv4.ip_local_port_range= 1024 65535
net.ipv4.tcp_mem= 786432 2097152 3145728
net.ipv4.tcp_rmem= 4096 4096 16777216
net.ipv4.tcp_wmem= 4096 4096 16777216
net.ipv4.tcp_max_orphans= 131072 
 

/etc/security/limits.conf 修改:

* soft nofile 1048576
* hardnofile 1048576
 

線上測試
使用ucloud雲主機進行測試,服務器配置:

啓動壓測前系統資源情況:


使用https://github.com/yedf/handy 庫自帶的測試程序,進行單機併發長連接測試,該庫在linux系統上使用epoll,在MacOSX上使用kqueue。

 

選取一臺主機S作爲服務器,運行服務器

10m/10m-svr100 300 10 301 #啓動10個子進程,每個進程分別監聽100-300的端口。


選取另一臺主機C作爲客戶端,運行客戶端,(S爲服務器的內網ip)

#啓動10個客戶端子進程,連接到S的100-300端口,發起10m個連接,在500秒內創建所有的連接,每600秒發送一個心跳,心跳數據爲64字節,多進程的管理端口爲301。

10m/10m-cli<S> 100 300 10000000 500 10 600 64 301


在服務器端使用watch ss –s  發現tcp連接數持續上升,最終穩定在4m左右


消耗資源:

系統佔用了20G左右的內存,但cpu佔用極少

   

客戶端報錯:

0m-cli:handy/logging.cc:164: void handy::Logger::logv(int, const char*, int, constchar*, const char*, ...): Assertion `0' failed.
2017/05/03-15:54:58.4048281a46 FATAL handy/poller.cc:35 epoll_ctl add failed 28 No space left on device
2017/05/03-15:54:58.4048121a40 FATAL handy/poller.cc:35 epoll_ctl add failed 28 No space left on device
2017/05/03-15:54:58.4048251a45 FATAL handy/poller.cc:35 epoll_ctl add failed 28 No space left on device

此錯誤一般是磁盤滿導致,但是在這裏是客戶端在進行epoll_ctl時,內存已滿導致註冊epoll事件失敗,所以客戶端此時停止繼續創建連接,可見此時的瓶頸出現在壓測的客戶端,如果客戶端內存夠用,理論上服務端10m個併發長連接應該可以實現。

 

說明:

單進程最大文件數量限制:ulimit -n 最多能把這個數字修改到1048575,因此單個進程最多能夠打開百萬個文件,千萬併發連接需要千萬個文件描述符,於是我們使用多進程來做到千萬文件的支持。

 

多進程之間的負載均衡:nginx使用多進程來增加自己的吞吐量,原先採用共享鎖的方式來平衡負載,對核數較多的服務器,較多的進程並沒有達到性能的線性提升。最新的linux內核引入了SO_REUSEPORT選項,該選項可以自動平衡監聽同一端口的多進程,是內核級的解決方案。handy採用該方案,優於nginx的舊有方式(最新的nginx也支持SO_REUSEPORT)。

 

測試中客戶端本地端口不夠:讓服務器監聽了200個端口,這樣客戶端連接服務器的每個端口只有50k個連接,然後加大默認的本地端口範圍就可以滿足要求(見前面的服務器系統參數)。

 

測試中如果一次性創建千萬個連接,則絕大部分的連接創建都會失敗,因此讓客戶端每100ms創建2000個連接,提高連接創建的成功率。

 

發佈了19 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章