NGINX 1.9.1 新特性:套接字端口共享
NGINX 1.9.1 發布版本中引入了一個新的特性 —— 允許套接字端口共享,該特性適用于大部分最新版本的操作系統,其中也包括 DragonFly BSD 和內核 3.9 以后的 Linux 操作系統。套接字端口共享選項允許多個套接字監聽同一個綁定的網絡地址和端口,這樣一來內核就可以將外部的請求連接負載均衡到這些套接字上來。(對于 NGINX Plus 的用戶來說,該特性將會在年底發布的版本 7 中得到支持)
實際上很多潛在的用戶希望使用端口重用功能。其他服務也可以簡單的進行熱更新升級(NGINX 支持 多種方式 的熱更新)。對于 NGINX 來說,打開該選項可以通過在特定場景下減少連接鎖競爭而提高性能。
如下圖所示,在該選項沒有生效的前提下,一個單獨的監聽套接字會通知所有的工作進程,每個進程則會試圖爭搶接管某個連接:
當該選項生效時,這個時候對于每個網絡地址和端口就會有多個監聽套接字,每個工作進程對應一個套接字,內核會決定由哪個監聽套接字(也就是決定哪 個工作進程)接管進來的連接。這個特性可以減少進程與進程之間為接收連接產生的鎖競爭而提高多核系統的性能。但是,如果當一個工作進程處于阻塞操作時,這 個時候不僅會影響已經被該進程接收的連接,還會阻塞由系統準備分配給該進程的連接請求:
配置套接字共享
如下面配置所示,可以通過在 listen 指令后添加新的參數 reuseport 來為 HTTP 或者 TCP( 流 模塊)打開套接字端口共享功能:
http { server { listen 80 reuseport; server_name localhost; ... } } stream { server { listen 12345 reuseport; ... } }
對于套接字來說,添加 reuseport 參數也就等于禁止了 accept_mutex 指令了,因為 mutex 對于 reuseport 來說是多余的,不過對于那些不希望使用套接字端口共享特性的端口來說,我們則有必要設置 accept_mutex。
reuseport 性能壓力測試
我通過運行 wrk 壓測工具來壓測一個跑在 36 核亞馬遜實例上并開啟 4 個工作進程的 NGINX。為了減少網絡對于測試結果的影響,我們的客戶端和 NGINX 都是運行在本地的,并且 NGINX 只返回 OK
字符串而不返回文件。我對比了三個不同的配置,一個是默認的(等同于 accept_mutex on),另一個是 accept_mutex off,還有一個則是 reuseport。正如下圖所示,使用 reuseport 后每秒能處理的請求數比其他的兩個增加了 2-3 倍,并且同時減少了平均延遲和平均延遲的標準差。
我還嘗試客戶端和 NGINX 分別運行在不同的主機上跑相關的壓力測試,但這一次 NGINX 返回的是一個 HTML 文件。正如下表所示,與上圖的結果類似,使用端口重用能減少請求的處理延遲,并且延遲標準差減少的更加明顯(大概是 10 倍)。其他結果(并沒有在下表中顯示)則更加讓人欣喜。通過端口重用,請求的壓力被各個工作進程均衡掉,而在默認條件下(也就是 accept_mutex 打開),一些工作進程會得到比較高的負載,而在 accept_mutex 關閉的時候,所有的進程都會顯示負載比較高。
在上述的壓力測試中,請求的頻率很高但是每個請求并不需要復雜的處理。其他一些初步的測試也表明端口重用特性在網絡流量匹配的時候最能提高性能。(該參數 并不能在郵件模塊的上下文監聽指令中生效,因為郵件的網絡流量并不能匹配這個特性)。我們建議你在使用 reuseport 這個指令前先在你的 NGINX 實例上進行性能測試,而不是全部的 NGINX 實例都使用。對于測試 NGINX 的一些小提示,可以參考 Konstantin Pavlov 在 2014 NGINX 大會上的演講。
致謝
感謝在英特爾工作的盧英奇和 Sepherosa Ziehau,他們每個人各自貢獻了使得套接字端口共享生效的解決方案。NGINX 開發小組合并了他們倆的想法從而創造出目前看來比較理想的解決方案。