nginx AIO機制與sendfile機制
從0.8.11版本開始, nginx 開始支持Linux native aio,如何在nginx里配置使用這套機制是本文介紹的重點。在下面的示例配置中,幾個重要相關選項的具體含義如下: aio:
Syntax: aio on | off | sendfile Default: off
在linux下aio可以設置為on或off(在freebsd還可以設置為sendfile),但需要2.6.22及以后版本的內核,原因在前面文章已經提到過,nginx使用的eventfd有這個 要求 。
directio:
Syntax: directio size | off Default: off
directio選項可以設置為off或一個大小值,表示當文件大小大于等于這個值時啟用directio。
sendfile:
Syntax: sendfile on | off Default: off
是否啟用sendfile()。
[root@www nginx-1.2.0]# uname -a Linux www.t1.com 2.6.38.8 #2 SMP Wed Nov 2 07:52:53 CST 2011 x86_64 x86_64 x86_64 GNU/Linux [root@www nginx-1.2.0]# cat /etc/issue CentOS Linux release 6.0 (Final) Kernel \r on an \m [root@www nginx-1.2.0]# ./configure --with-file-aio [root@www nginx-1.2.0]# make [root@www nginx-1.2.0]# make install [root@www nginx-1.2.0]# cat /usr/local/nginx/conf/nginx.conf ... http { ... server { ... location / { aio on; directio 512k; sendfile on; output_buffers 1 128k; ...
要使aio生效需把directio設置為打開狀況,并且如果aio生效,那么將自動不使用sendfile(),這在linux下這是顯然的,要么利用aio讀到緩存區,要么利用sendfile()直接發送出去,兩者不可兼用,而對于freebsd系統下aio和sendfile并用的情況,我并不了解,所以也就不妄加評論;
可以看到directio是針對每個請求的文件大小而決定是否開啟directio的,因此對于上面的整個示例配置,也就會針對每個請求的不同而不同: 如果某處請求的文件大于等于512k,那么將啟用directio,從而aio生效,進而sendfile不生效; 如果某處請求的文件小于512k,那么將禁用directio,從而aio也就不生效,轉而使用sendfile(),即sendfile生效;
這種設計貌似剛好把linux下aio和sendfile兩種機制的優點很好的結合起來使用。對于大文件采用aio,節省cpu,而對于小文件,采用sendfile,減少拷貝;并且對于大文件aio采用directio,避免擠占文件系統緩存,讓文件系統緩存更多的小文件。 從理論上來看,這種配置比較適合系統內存有限、小文件請求比較多、間隔有幾個大文件請求的Web環境;如果內存足夠大,那么應該充分利用文件系統緩存,而directio使得aio無法使用緩存是衡量最終是否需要采用aio的一個需要仔細考慮的因素;網上有人總結說nginx+aio很好,也有人說其很差,其實根據特定的系統環境和應用場景來做配置調節,才能達到性能的最優,nginx提供的 AIO 只是一套工具,沒有固定的好與差之分,就看你能否恰當的用好它,但據nginx官網論壇來看,在linux系統的大部分場景下,目前因使用aio功能附加的限制而帶來的實際效果估計并不太理想:
nginx supports file AIO only in 0.8.11+, but the file AIO is functional on FreeBSD only. On Linux AIO is supported by nginx only on kerenl 2.6.22+ (although, CentOS 5.5 has backported the required AIO features). Anyway, on Linux AIO works only if file offset and size are aligned to a disk block size (usually 512 bytes) and this data can not be cached in OS VM cache (Linux AIO requires DIRECTIO that bypass OS VM cache). I believe a cause of so strange AIO implementaion is that AIO in Linux was developed mainly for databases by Oracle and IBM.
示例:
location /video/ { sendfile on; sendfile_max_chunk 256k; aio threads; directio 512k; output_buffers 1 128k; }
啟用aio時會自動啟用directio,小于directio定義的大小的文件則采用sendfile進行發送,超過或等于directio定義的大小的文件,將采用aio線程池進行發送,也就是說aio和directio適合大文件下載.因為大文件不適合進入操作系統的buffers/cache,這樣會浪費內存,而且Linux AIO(異步磁盤IO)也要求使用directio的形式.
sendfilemaxchunk可以減少阻塞調用sendfile()所花費的最長時間.因為Nginx不會嘗試一次將整個文件發送出去,而是每次發送大小為256KB的塊數據.
注意,Nginx從1.7.11開始為AIO引入了線程池支持,能夠使用多線程讀取和發送文件,以免工人進程被阻塞.要啟用多線程支持,configure時需要顯式加入–with-threads選項.
sendfile機制:
在apache,nginx,lighttpd等web服務器當中,都有一項sendfile相關的配置,在一些網上的資料都有談到sendfile會提升文件傳輸性能,那sendfile到底是什么呢?它的原理又是如何呢?
在傳統的文件傳輸里面(read/write方式),在實現上其實是比較復雜的,需要經過多次上下文的切換,我們看一下如下兩行代碼: Java代碼 收藏代碼
read(file, tmp_buf, len); write(socket, tmp_buf, len); 以上兩行代碼是傳統的read/write方式進行文件到socket的傳輸。
當需要對一個文件進行傳輸的時候,其具體流程細節如下: 1、調用read函數,文件數據被copy到內核緩沖區 2、read函數返回,文件數據從內核緩沖區copy到用戶緩沖區 3、write函數調用,將文件數據從用戶緩沖區copy到內核與socket相關的緩沖區。 4、數據從socket緩沖區copy到相關協議引擎。
以上細節是傳統read/write方式進行網絡文件傳輸的方式,我們可以看到,在這個過程當中,文件數據實際上是經過了四次copy操作:
硬盤—>內核buf—>用戶buf—>socket相關緩沖區—>協議引擎
而sendfile系統調用則提供了一種減少以上多次copy,提升文件傳輸性能的方法。Sendfile系統調用是在2.1版本內核時引進的: Java代碼 收藏代碼
sendfile(socket, file, len);
運行流程如下: 1、sendfile系統調用,文件數據被copy至內核緩沖區 2、再從內核緩沖區copy至內核中socket相關的緩沖區 3、最后再socket相關的緩沖區copy到協議引擎
相較傳統read/write方式,2.1版本內核引進的sendfile已經減少了內核緩沖區到user緩沖區,再由user緩沖區到socket相關緩沖區的文件copy,而在內核版本2.4之后,文件描述符結果被改變,sendfile實現了更簡單的方式,系統調用方式仍然一樣,細節與2.1版本的不同之處在于,當文件數據被復制到內核緩沖區時,不再將所有數據copy到socket相關的緩沖區,而是僅僅將記錄數據位置和長度相關的數據保存到socket相關的緩存,而實際數據將由DMA模塊直接發送到協議引擎,再次減少了一次copy操作。
原創文章,作者:YOUNG,如若轉載,請注明出處:http://www.www58058.com/54790