原創:小姐姐味道(微信公衆號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模式),而是用來處理本地的一些靜態文件。微信
這裏涉及到幾個基本指令:sendfile
、aio
和directio
,它們均與本地文件的操做有關,接下來咱們分別看看它的意義。網絡
這個指令與系統函數sendfile()
具備相同的語義,sendfile的目的就是提升本地文件經過socket發送的效率。官方的博客介紹瞭如何利用nginx 線程池aio,實現9倍
的性能。多線程
它還有一個比較好記的名稱,叫作零拷貝
。那與傳統的文件讀取而後發送到網絡上,有什麼區別呢?架構
磁盤、網絡驅動器、內存是三種不一樣的傳輸介質,若是從本地讀取一個文件並經過socket發送出去,一般狀況下是進過以下幾個步驟:併發
能夠看到,數據的發送過程涉及到屢次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;
}
複製代碼
這個指令用於開啓對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模型,其實語義也基本相同,就是異步文件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
配置來開啓,此特性盡在支持epoll
、kqueue
的平臺上兼容。對於線程池的設置,咱們能夠經過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。
nginx官方宣稱使用多線程模式,在aio讀取文件場景下,性能有9倍的提高,但我仍是對這個測試具備必定懷疑態度。
多線程 + aio在必定程度的確能夠提升文件IO的讀取性能,可是對於大文件而言,這彷佛並無想象的那麼優秀。這受制於linux平臺底層的自己特性,除非nginx本身對文件cache作了額外的操做。
到目前爲止,xjjdog仍有如下建議(供參考):
做者簡介:小姐姐味道 (xjjdog),一個不容許程序員走彎路的公衆號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高併發世界,給你不同的味道。個人我的微信xjjdog0,歡迎添加好友,進一步交流。