為高負載網路最佳化Nginx和Node.js的方法

來源:互聯網
上載者:User

在搭建高輸送量web應用這個議題上,NginX和Node.js可謂是天生一對。他們都是基於事件驅動模型而設計,可以輕易突破 Apache等傳統web伺服器的C10K瓶頸。預設的配置已經可以獲得很高的並發,不過,要是大家想在廉價硬體上做到每秒數千以上的請求,還是有一些工作要做的。

這篇文章假定讀者們使用NginX的HttpProxyModule來為上遊的node.js伺服器充當反向 Proxy。我們將介紹Ubuntu 10.04以上系統sysctl的調優,以及node.js應用與NginX的調優。當然,如果大家用的是Debian系統,也能達到同樣的目標,只不過調優的方法有所不同而已。

網路調優

如果不先對Nginx和Node.js的底層傳輸機制有所瞭解,並進行針對性最佳化,可能對兩者再細緻的調優也會徒勞無功。一般情況下,Nginx通過TCP socket來串連用戶端與上遊應用。

我們的系統對TCP有許多門限值與限制,通過核心參數來設定。這些參數的預設值往往是為一般的用途而定的,並不能滿足web伺服器所需的高流量、短生命的要求。

這裡列出了調優TCP可供候選的一些參數。為使它們生效,可以將它們放在/etc/sysctl.conf檔案裡,或者放入一個新設定檔,比如 /etc/sysctl.d/99-tuning.conf,然後運行sysctl -p,讓核心裝載它們。我們是用sysctl-cookbook來幹這個體力活。

需要注意的是,這裡列出來的值是可以安全使用的,但還是建議大家研究一下每個參數的含義,以便根據自己的負荷、硬體和使用方式選擇一個更加合適的值。

複製代碼 代碼如下:<SPAN style="FONT-SIZE: 14px; COLOR: #009900; FONT-FAMILY: Microsoft YaHei"> net.ipv4.ip_local_port_range='1024 65000'
net.ipv4.tcp_tw_reuse='1'
net.ipv4.tcp_fin_timeout='15'
net.core.netdev_max_backlog='4096'
net.core.rmem_max='16777216'
net.core.somaxconn='4096'
net.core.wmem_max='16777216'
net.ipv4.tcp_max_syn_backlog='20480'
net.ipv4.tcp_max_tw_buckets='400000'
net.ipv4.tcp_no_metrics_save='1'
net.ipv4.tcp_rmem='4096 87380 16777216'
net.ipv4.tcp_syn_retries='2'
net.ipv4.tcp_synack_retries='2'
net.ipv4.tcp_wmem='4096 65536 16777216'
vm.min_free_kbytes='65536' </SPAN>

重點說明其中幾個重要的。

net.ipv4.ip_local_port_range

為了替上遊的應用服務下遊的用戶端,NginX必須開啟兩條TCP串連,一條串連用戶端,一條串連應用。在伺服器收到很多串連時,系統的可用連接埠將很快被耗盡。通過修改net.ipv4.ip_local_port_range參數,可以將可用連接埠的範圍改大。如果在/var/log/syslog 中發現有這樣的錯誤: “possible SYN flooding on port 80. Sending cookies”,即表明系統找不到可用連接埠。增大net.ipv4.ip_local_port_range參數可以減少這個錯誤。

net.ipv4.tcp_tw_reuse

當伺服器需要在大量TCP串連之間切換時,會產生大量處於TIME_WAIT狀態的串連。TIME_WAIT意味著串連本身是關閉的,但資源還沒有釋放。將net_ipv4_tcp_tw_reuse設定為1是讓核心在安全時盡量回收串連,這比重建立立新串連要便宜得多。

net.ipv4.tcp_fin_timeout

這是處於TIME_WAIT狀態的串連在回收前必須等待的最小時間。改小它可以加快回收。

如何檢查串連狀態

使用netstat:

netstat -tan | awk '{print $6}' | sort | uniq -c

或使用ss:

ss -s

NginX

ss -s
Total: 388 (kernel 541)
TCP: 47461 (estab 311, closed 47135, orphaned 4, synrecv 0, timewait 47135/0), ports 33938

Transport Total IP IPv6
* 541 - -
RAW 0 0 0
UDP 13 10 3
TCP 326 325 1
INET 339 335 4
FRAG 0 0 0

隨著web伺服器的負載逐漸升高,我們就會開始遭遇NginX的某些奇怪限制。串連被丟棄,核心不停報SYN flood。而這時,平均負荷和CPU使用率都很小,伺服器明明是可以處理更多串連的狀態,真令人沮喪。

經過調查,發現有非常多處於TIME_WAIT狀態的串連。這是其中一個伺服器的輸出:

有47135個TIME_WAIT串連!而且,從ss可以看出,它們都是已經關閉的串連。這說明,伺服器已經消耗了絕大部分可用連接埠,同時也暗示我們,伺服器是為每個串連都分配了新連接埠。調優網路對這個問題有一點協助,但是連接埠仍然不夠用。

經過繼續研究,我找到了一個關於上行串連keepalive指令的文檔,它寫道:

設定通往上遊伺服器的最大空閑保活串連數,這些串連會被保留在背景工作處理序的緩衝中。

有趣。理論上,這個設定是通過在緩衝的串連上傳遞請求來儘可能減少串連的浪費。文檔中還提到,我們應該把proxy_http_version設為"1.1",並清除"Connection"頭部。經過進一步的研究,我發現這是一種很好的想法,因為HTTP/1.1相比HTTP1.0,大大最佳化了 TCP串連的使用率,而Nginx預設用的是HTTP/1.0。

按文檔的建議修改後,我們的上行設定檔變成這樣: 複製代碼 代碼如下:upstream backend_nodejs {
server nodejs-3:5016 max_fails=0 fail_timeout=10s;
server nodejs-4:5016 max_fails=0 fail_timeout=10s;
server nodejs-5:5016 max_fails=0 fail_timeout=10s;
server nodejs-6:5016 max_fails=0 fail_timeout=10s;
keepalive 512;
}

我還按它的建議修改了server一節的proxy設定。同時,加了一個 p roxy_next_upstream來跳過故障的伺服器,調整了用戶端的 keepalive_timeout,並關閉訪問日誌。配置變成這樣: 複製代碼 代碼如下:server {
listen 80;
server_name fast.gosquared.com;

client_max_body_size 16M;
keepalive_timeout 10;

location / {
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_pass http://backend_nodejs;
}

access_log off;
error_log /dev/null crit;
}

採用新的配置後,我探索服務器們佔用的socket 降低了90%。現在可以用少得多的串連來傳輸請求了。新的輸出如下:

ss -s

Total: 558 (kernel 604)
TCP: 4675 (estab 485, closed 4183, orphaned 0, synrecv 0, timewait 4183/0), ports 2768

Transport Total IP IPv6
* 604 - -
RAW 0 0 0
UDP 13 10 3
TCP 492 491 1
INET 505 501 4

Node.js

得益於事件驅動式設計可以非同步處理I/O,Node.js開箱即可處理大量的串連和請求。雖然有其它一些調優手段,但這篇文章將主要關注node.js的進程方面。

Node是單線程的,不會自動使用多核。也就是說,應用不能自動獲得伺服器的全部能力。

實現Node進程的叢集化

我們可以修改應用,讓它fork多個線程,在同一個連接埠上接收資料,從而實現負載的跨越多核。Node有一個cluster模組,提供了實現這個目標所必需的所有工具,但要將它們加入應用中還需要很多體力活。如果你用的是express,eBay有一個叫cluster2的模組可以用。

防止環境切換

當運行多個進程時,應該確保每個CPU核同一時間只忙於一個進程。一般來說,如果CPU有N個核,我們應該產生N-1個應用進程。這樣可以確保每個進程都能得到合理的時間片,而剩下的一個核留給核心發送器運行其它任務。我們還要確保伺服器上基本不執行除Node.js外的其它任務,防止出現CPU 的爭用。

我們曾經犯過一個錯誤,在伺服器上部署了兩個node.js應用,然後每個應用都開了N-1個進程。結果,它們互相之間搶奪CPU,導致系統的負荷急升。雖然我們的伺服器都是8核的機器,但仍然可以明顯地感覺到由環境切換引起的效能開銷。環境切換是指CPU為了執行其它任務而掛起當前任務的現象。在切換時,核心必須掛起當前進程的所有狀態,然後裝載和執行另一個進程。為瞭解決這個問題,我們減少了每個應用開啟的進程數,讓它們公平地分享 CPU,結果系統負荷就降了下來:

請注意,看系統負荷(藍線)是如何降到CPU核心數(紅線)以下的。在其它伺服器上,我們也看到了同樣的情況。既然總的工作量保持不變,那麼中的效能改善只能歸功於環境切換的減少。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.