nginx「線程池模式」探討;聽說有9倍性能提高

原創:小姐姐味道(微信公衆號ID:xjjdog),歡迎分享,轉載請保留出處。html

相對於Apache的同步IO模型,Nginx因爲採用了NIO的緣故,性能上碾壓前者。Nginx是輕量級的,佔用的系統資源更少,自然支持高併發。linux

今天咱們就簡單的討論一下nginx的線程模型。注意不是進程模型哦。nginx

nginx的IO模型,你們應該都有所瞭解。簡單而言,就是一個master進程和多個worker進程(進程數由配置決定);master進程負責accept請求並隊列化,最後轉發給worker進程並由其進行請求處理和響應的整個過程。程序員

nginx是以多進程模式運行的。nginx在1.7.11版本提供了多線程特性(multi-threading),不過這個多線程僅用在aio模型中對本地文件的操做上,出發點就是以非阻塞模式,來提升文件IO的效率和併發能力。緩存

因此這個多線程,並非nginx經過多線程的方式處理proxy request(這部分是經過epoll模式),而是用來處理本地的一些靜態文件。微信

這裏涉及到幾個基本指令:sendfileaiodirectio,它們均與本地文件的操做有關,接下來咱們分別看看它的意義。網絡

sendfile

這個指令與系統函數sendfile()具備相同的語義,sendfile的目的就是提升本地文件經過socket發送的效率。官方的博客介紹瞭如何利用nginx 線程池aio,實現9倍的性能。多線程

它還有一個比較好記的名稱,叫作零拷貝。那與傳統的文件讀取而後發送到網絡上,有什麼區別呢?架構

磁盤、網絡驅動器、內存是三種不一樣的傳輸介質,若是從本地讀取一個文件並經過socket發送出去,一般狀況下是進過以下幾個步驟:併發

  • 1)磁盤驅動器從根據CPU的調度,從磁盤讀取必定長度(chunk)的字節數據
  • 2)字節數據copy到內核內存中
  • 3)將內核內存中的數據copy到進程工做區內存
  • 4)進程經過socket將數據copy到網絡驅動器緩存, 並經過相應的傳輸協議發送出去。

能夠看到,數據的發送過程涉及到屢次copy,這受限於計算機系統的設計問題。

sendfile的主要出發點,就是要減小數據的copy以提升發送效率,sendfile是linux系統級的調用,socket能夠經過DMA(直接內存訪問)方式直接訪問文件數據,並經過傳輸協議發送,減小了2次數據copy(磁盤到內核,內核到工做區)。

sendfile_max_chunk參數用於限定每次sendfile()調用發送的最大數據尺寸,若是不限制大小的話,將會獨佔整個worker進程,默認爲「無限制」。這也太霸道了。

對於nginx而言,代理靜態本地的靜態文件資源(一般是小文件)將是很是高效的,建議對一些靜態文件好比html、圖片等,開啓此參數。

location /video {  
    sendfile on;  
    sendfile_max_chunk 128k;  
    aio on;  
}  
複製代碼

directio

這個指令用於開啓對O_DIRECT標記(BSD,linux)的使用,對應directio()這個系統調用。

此參數是針對大文件而設定的,sendfile針對的是小文件。經過directio能夠指定限定的尺寸大小,對於超過此size的文件,將會使用directio(而再也不使用sendfile)。

根據directio的設計初衷,它具有sendfile的基本原理,只是不使用內核cache,而是直接使用DMA,並且使用以後內存cache(頁對齊部分)也將被釋放。

所以directio一般適用於大文件讀取,並且一般讀取頻率很低。由於對於高頻的讀取,它並不能提升效率(由於它不會重用cache,而是每次都DMA)。因爲存在性能權衡問題,此參數默認爲off。

location /video {  
    sendfile on;  
    directio 8m;  
    aio on;  
}  
複製代碼

aio

談到aio模型,其實語義也基本相同,就是異步文件IO,nginx默認關閉此特性,它須要在高版本的linux平臺上才支持(2.6.22+)。

在linux上,directio只能讀取基於512字節邊界對齊的blocks,文件結束的那些未對齊的block將使用阻塞模式讀取。

一樣,若是文件在開頭沒有對齊,整個文件都將阻塞式讀取。這裏所謂的對齊,就是文件數據在內存頁中的cache狀況。

當aio和sendfile都開啓時,將會對那些size大於directio設定值的文件使用aio機制:即當小於directio設定值的文件將直接使用sendfile(aio不參與)。

aio,簡單而言,就是使用多線程異步模式讀取較大的文件,以提升IO效率,可是事實上可能並無任何提升。由於大文件的讀取,並不能使用cache、並且自己也是耗時的,即便是多線程,對於request的等待時間也是沒法預估的,特別是併發請求較高的時候。可是aio可以提升IO的併發能力,這個是肯定的。

默認狀況下,多線程模式是關閉的,咱們須要經過--with-threads配置來開啓,此特性盡在支持epollkqueue的平臺上兼容。對於線程池的設置,咱們能夠經過thread_pool來聲明,並在aio指令中指定。

咱們的配置文件近一步豐富了一些。

thread_pool default_pool threads=16;##main上下文  
...  
location /video {  
    sendfile on;  
    sendfile_max_chunk 128k;  
    directio 8M;  
    aio threads=default_pool;  
}  
複製代碼

當線程池中全部的線程都處於busy狀態,那麼新的task請求將會加入到等待隊列。咱們能夠在thread_pool中使用max_queue參數來指定隊列的大小,默認隊列大小爲65536,當隊列已滿後續的請求將會拋出error。

END

nginx官方宣稱使用多線程模式,在aio讀取文件場景下,性能有9倍的提高,但我仍是對這個測試具備必定懷疑態度。

多線程 + aio在必定程度的確能夠提升文件IO的讀取性能,可是對於大文件而言,這彷佛並無想象的那麼優秀。這受制於linux平臺底層的自己特性,除非nginx本身對文件cache作了額外的操做。

到目前爲止,xjjdog仍有如下建議(供參考):

  • 1)對於小文件的靜態代理,咱們應該開啓sendfile,這對性能的提高是顯著的。
  • 2)對於大文件讀取(低頻),咱們能夠嘗試開啓aio、directio,在提高併發能力的前提下,關注request的實際響應效率;既然官方推薦這麼用,咱們能夠報以嘗試的態度。
  • 3)對於高頻大文件讀取,aio、directio的性能或許提高並不顯著,但應該不會下降性能。

做者簡介:小姐姐味道 (xjjdog),一個不容許程序員走彎路的公衆號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高併發世界,給你不同的味道。個人我的微信xjjdog0,歡迎添加好友,​進一步交流。​

相關文章
相關標籤/搜索