(轉)http長鏈接200萬嘗試及調優
原文在:http://rdc.taobao.com/blog/cs/?p=1062 其中講到了不少TCP的調整參數,現轉之,十分精彩 對於一個server,咱們通常考慮他所能支撐的qps,但有那麼一種應用, 咱們須要關注的是它能支撐的鏈接數個數,而並不是qps,固然qps也是咱們須要考慮的性能點之一。這種應用常見於消息推送系統,也稱爲comet應用,好比聊天室或即時消息推送系統等。comet應用詳細介紹可見我以前的介紹,在此很少講。對於這類系統,由於不少消息須要到產生時才推送給客戶端,因此當沒有消息產生時,就須要hold住客戶端的鏈接,這樣,當有大量的客戶端時,就須要hold住大量的鏈接,這種鏈接咱們稱爲長鏈接。 首先,咱們分析一下,對於這類服務,需消耗的系統資源有:cpu、網絡、內存。因此,想讓系統性能達到最佳,咱們先找到系統的瓶頸所在。這樣的長鏈接,每每咱們是沒有數據發送的,因此也能夠看做爲非活動鏈接。對於系統來講,這種非活動鏈接,並不佔用cpu與網絡資源,而僅僅佔用系統的內存而已。因此,咱們假想,只要系統內存足夠,系統就可以支持咱們想達到的鏈接數,那麼事實是否真的如此?若是真能這樣,內核來維護這至關大的數據結構,也是一種考驗。 要完成測試,咱們須要有一個服務端,還有大量的客戶端。因此須要服務端程序與客戶端程序。爲達到目標,個人想法是這樣的:客戶端產生一個鏈接,向服務端發起一個請求,服務端hold住該鏈接,而不返回數據。 1. 服務端的準備 對於服務端,因爲以前的假想,咱們須要一臺大內存的服務器,用於部署nginx的comet應用。下面是我用的服務端的狀況: ◦Summary: Dell R710, 2 x Xeon E5520 2.27GHz, 23.5GB / 24GB 1333MHz ◦System: Dell PowerEdge R710 (Dell 0VWN1R) ◦Processors: 2 x Xeon E5520 2.27GHz 5860MHz FSB (16 cores) ◦Memory: 23.5GB / 24GB 1333MHz == 6 x 4GB, 12 x empty ◦Disk-Control: megaraid_sas0: Dell/LSILogic PERC 6/i, Package 6.2.0-0013, FW 1.22.02-0612, ◦Network: eth0 (bnx2):Broadcom NetXtreme II BCM5709 Gigabit Ethernet,1000Mb/s ◦OS: RHEL Server 5.4 (Tikanga), Linux 2.6.18-164.el5 x86_64, 64-bit 服務端程序很簡單,基於nginx寫的一個comet模塊,該模塊接受用戶的請求,而後保持用戶的鏈接,而不返回。Nginx的status模塊,可直接用於監控最大鏈接數。 服務端還須要調整一下系統的參數,在/etc/sysctl.conf中: ◦net.core.somaxconn = 2048 ◦net.core.rmem_default = 262144 ◦net.core.wmem_default = 262144 ◦net.core.rmem_max = 16777216 ◦net.core.wmem_max = 16777216 ◦net.ipv4.tcp_rmem = 4096 4096 16777216 ◦net.ipv4.tcp_wmem = 4096 4096 16777216 ◦net.ipv4.tcp_mem = 786432 2097152 3145728 ◦net.ipv4.tcp_max_syn_backlog = 16384 ◦net.core.netdev_max_backlog = 20000 ◦net.ipv4.tcp_fin_timeout = 15 ◦net.ipv4.tcp_max_syn_backlog = 16384 ◦net.ipv4.tcp_tw_reuse = 1 ◦net.ipv4.tcp_tw_recycle = 1 ◦net.ipv4.tcp_max_orphans = 131072 ◦ ◦/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的參數: ◦net.ipv4.ip_local_port_range = 1024 65535 ◦ ◦/sbin/sysctl -p 客戶端程序是基於libevent寫的一個測試程序,不斷的創建新的鏈接請求。 3. 因爲客戶端與服務端須要創建大量的socket,因此咱們須要調速一下最大文件描述符。 客戶端,須要建立六萬多個socket,我設置最大爲十萬好了,的在/etc/security/limits.conf中添加: ◦admin soft nofile 100000 ◦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 升級內核後,繼續咱們的調優,以下: sudo bash -c ‘echo 2000000 > /proc/sys/fs/nr_open’ 如今再設置nofile就能夠了 ◦admin soft nofile 2000000 ◦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的監控獲得數據: 兩百萬鏈接時系統內存狀況: 5. 正常,咱們在線上配置時,Nginx的request_pool_size須要根據實際狀況進行調整。net.ipv4.tcp_rmem 與net.ipv4.tcp_wmem的默認大小也須要調整。