http長連接200萬嘗試及調優


對於一個server,我們一般考慮他所能支撐的qps,但有那么一種應用, 我們需要關注的是它能支撐的連接數個數,而並非qps,當然qps也是我們需要考慮的性能點之一。這種應用常見於消息推送系統,也稱為comet應用,比如聊天室或即時消息推送系統等。comet應用具體可見我之前的介紹,在此不多講。對於這類系統,因為很多消息需要到產生時才推送給客戶端,所以當沒有消息產生時,就需要hold住客戶端的連接,這樣,當有大量的客戶端時,就需要hold住大量的連接,這種連接我們稱為長連接。

首先,我們分析一下,對於這類服務,需消耗的系統資源有:cpu、網絡、內存。所以,想讓系統性能達到最佳,我們先找到系統的瓶頸所在。這樣的長連接,往往我們是沒有數據發送的,所以也可以看作為非活動連接。對於系統來說,這種非活動連接,並不占用cpu與網絡資源,而僅僅占用系統的內存而已。所以,我們假想,只要系統內存足夠,系統就能夠支持我們想達到的連接數,那么事實是否真的如此?如果真能這樣,內核來維護這相當大的數據結構,也是一種考驗。

要完成測試,我們需要有一個服務端,還有大量的客戶端。所以需要服務端程序與客戶端程序。為達到目標,我的想法是這樣的:客戶端產生一個連接,向服務端發起一個請求,服務端hold住該連接,而不返回數據。

1. 服務端的准備
對於服務端,由於之前的假想,我們需要一台大內存的服務器,用於部署nginx的comet應用。下面是我用的服務端的情況:

  1. Summary:        Dell R710, 2 x Xeon E5520 2.27GHz, 23.5GB / 24GB 1333MHz  
  2. System:         Dell PowerEdge R710 (Dell 0VWN1R)  
  3. Processors:     2 x Xeon E5520 2.27GHz 5860MHz FSB (16 cores)  
  4. Memory:         23.5GB / 24GB 1333MHz == 6 x 4GB, 12 x empty  
  5. Disk-Control:   megaraid_sas0: Dell/LSILogic PERC 6/i, Package 6.2.0-0013, FW 1.22.02-0612,  
  6. Network:     eth0 (bnx2):Broadcom NetXtreme II BCM5709 Gigabit Ethernet,1000Mb/s  
  7. OS:             RHEL Server 5.4 (Tikanga), Linux 2.6.18-164.el5 x86_64, 64-bit  

服務端程序很簡單,基於nginx寫的一個comet模塊,該模塊接受用戶的請求,然后保持用戶的連接,而不返回。Nginx的status模塊,可直接用於監控最大連接數。

服務端還需要調整一下系統的參數,在/etc/sysctl.conf中:

  1. net.core.somaxconn = 2048  
  2. net.core.rmem_default = 262144  
  3. net.core.wmem_default = 262144  
  4. net.core.rmem_max = 16777216  
  5. net.core.wmem_max = 16777216  
  6. net.ipv4.tcp_rmem = 4096 4096 16777216  
  7. net.ipv4.tcp_wmem = 4096 4096 16777216  
  8. net.ipv4.tcp_mem = 786432 2097152 3145728  
  9. net.ipv4.tcp_max_syn_backlog = 16384  
  10. net.core.netdev_max_backlog = 20000  
  11. net.ipv4.tcp_fin_timeout = 15  
  12. net.ipv4.tcp_max_syn_backlog = 16384  
  13. net.ipv4.tcp_tw_reuse = 1  
  14. net.ipv4.tcp_tw_recycle = 1  
  15. net.ipv4.tcp_max_orphans = 131072  
  16.   
  17. /sbin/sysctl -p 生效  

這里,我們主要看這幾項:
net.ipv4.tcp_rmem 用來配置讀緩沖的大小,三個值,第一個是這個讀緩沖的最小值,第三個是最大值,中間的是默認值。我們可以在程序中修改讀緩沖的大小,但是不能超過最小與最大。為了使每個socket所使用的內存數最小,我這里設置默認值為4096。
net.ipv4.tcp_wmem 用來配置寫緩沖的大小。
讀緩沖與寫緩沖在大小,直接影響到socket在內核中內存的占用。
而net.ipv4.tcp_mem則是配置tcp的內存大小,其單位是頁,而不是字節。當超過第二個值時,TCP進入pressure模式,此時TCP嘗試穩定其內存的使用,當小於第一個值時,就退出pressure模式。當內存占用超過第三個值時,TCP就拒絕分配socket了,查看dmesg,會打出很多的日志“TCP: too many of orphaned sockets”。
另外net.ipv4.tcp_max_orphans這個值也要設置一下,這個值表示系統所能處理不屬於任何進程的socket數量,當我們需要快速建立大量連接時,就需要關注下這個值了。當不屬於任何進程的socket的數量大於這個值時,dmesg就會看到”too many of orphaned sockets”。

另外,服務端需要打開大量的文件描述符,比如200萬個,但我們設置最大文件描述符限制時,會遇到一些問題,我們在后面詳細講解。

2. 客戶端的准備
由於我們需要構建大量的客戶端,而我們知道,在一台系統上,連接到一個服務時的本地端口是有限的。由於端口是16位整數,也就只能是0到65535,而0到1023是預留端口,所以能分配的只是1024到65534,也就是64511個。也就是說,一台機器只能創建六萬多個長連接。要達到我們的兩百萬連接,需要大概34台客戶端。
當然,我們可以采用虛擬ip的方式來實現這么多客戶端,如果是虛擬ip,則每個ip可以綁定六萬多個端口,34個虛擬ip就可以搞定。而我這里呢,正好申請到了公司的資源,所以就采用實體機來做了。
由於系統默認參數,自動分配的端口數有限,是從32768到61000,所以我們需要更改客戶端/etc/sysctl.conf的參數:

  1. net.ipv4.ip_local_port_range = 1024 65535  
  2.   
  3. /sbin/sysctl -p  

客戶端程序是基於libevent寫的一個測試程序,不斷的建立新的連接請求。

3. 由於客戶端與服務端需要建立大量的socket,所以我們需要調速一下最大文件描述符。
客戶端,需要創建六萬多個socket,我設置最大為十萬好了,的在/etc/security/limits.conf中添加:

  1. admin    soft    nofile  100000  
  2. admin    hard    nofile  100000  

服務端,需要創建200萬連接,那我想設置nofile為200萬,好,問題來了。
當我設置nofile為200萬時,系統直接無法登陸了。嘗試幾次,發現最大只能設置到100萬。在查過源碼后,才知道,原來在2.6.25內核之前有個宏定義,定義了這個值的最大值,為1024*1024,正好是100萬,而在2.6.25內核及其之后,這個值是可以通過/proc/sys/fs/nr_open來設置。於是我升級內核到2.6.32。ulimit詳細介紹見博文:老生常談: ulimit問題及其影響: http://blog.yufeng.info/archives/1380
升級內核后,繼續我們的調優,如下:

  1. sudo bash -c 'echo 2000000 > /proc/sys/fs/nr_open'  

現在再設置nofile就可以了

  1. admin    soft    nofile  2000000  
  2. admin    hard    nofile  2000000  

4. 最后,在測試的過程中,根據dmesg的系統打出的信息不斷調整服務端/sbin/sysctl中的配置,最后我們的測試完成了200萬的長連接。
為了使內存占用盡量減少,我將Nginx的request_pool_size從默認的4k改成1k了。另外,net.ipv4.tcp_wmem與net.ipv4.tcp_rmem中的默認值也設置成4k。

兩百萬連接時,通過nginx的監控得到數據:
兩百萬連接數

兩百萬連接時系統內存情況:
內存占用


免責聲明!

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



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