Nginx學(xué)習(xí):HTTP核心模塊(八)文件處理
繼續(xù)我們的 HTTP 核心模塊之旅。今天主要是文件相關(guān)的一些處理操作,包括 DirectIO、文件緩存以及 sendfile 相關(guān)的配置。這三個配置中,大家應(yīng)該會見過 sendfile ,但是另外兩個就比較少見了。包括我之前也從來沒見過,不過還好,DirectIO 并不是一個完全的陌生人,文件緩存優(yōu)化也與操作系統(tǒng)基礎(chǔ)知識有關(guān),而 sendfile 一般默認就是開啟的,所以大家也不要有太大的壓力哦。
directio
是不是看著很眼熟,沒錯,早前我們在 PHP 的小課堂文章中學(xué)習(xí)過。沒印象或者想不起來的小伙伴可以移步?PHP中DirectIO直操作文件擴展的使用https://mp.weixin.qq.com/s/fS6X2IlrnrBrBZwwqRF3vA?(去博客搜索也可以哦) 去回憶一下哦。別的不多說了,直接來看看相關(guān)的配置。由于不知道要怎么測試,所以就簡單地介紹一些這些配置就好了,如果有小伙伴了解這一塊要怎么測試的,可以評論區(qū)留言哦。
directio
當(dāng)讀入長度大于等于指定 size 的文件時,開啟 DirectIO 功能。
directio?size?|?off;
具體的做法是, 在 FreeBSD 或 Linux 系統(tǒng)開啟使用 O_DIRECT 標(biāo)志, 在 Mac OS X 系統(tǒng)開啟使用 F_NOCACHE 標(biāo)志, 在 Solaris 系統(tǒng)開啟使用 directio() 功能。這條指令自動關(guān)閉 sendfile(0.7.15版) 。它在處理大文件時 directio 4m; 或者在 Linux 系統(tǒng)使用 aio 時比較有用。默認 off 。
directio_alignment
為 DirectIO 設(shè)置文件偏移量對齊。
directio_alignment?size;
大多數(shù)情況下,按512字節(jié)對齊足矣, 但在 Linux 系統(tǒng)下使用 XFS ,需要將值擴大到 4K 。
文件優(yōu)化緩存
這個緩存是個什么東西呢?它可以用于減少 Nginx 的系統(tǒng)調(diào)用,緩存文件句柄、大小和修改時間等。具體作用我們在最后會看到。
open_file_cache
用于配置文件緩存。
open_file_cache?off;
open_file_cache?max=N?[inactive=time];
默認是 off ,也就是關(guān)閉狀態(tài)的??膳渲玫闹蛋ǎ?/p>
max=N,設(shè)置緩存中元素的最大數(shù)量,當(dāng)緩存溢出時,使用 LRU(最近最少使用) 算法刪除緩存中的元素
inactive=time,在這段時間內(nèi)緩存元素如果沒有被訪問,將從緩存中刪除。默認超時是60秒
它可以緩存的內(nèi)容包括:
打開文件的描述符,大小和修改時間
目錄查找結(jié)果
文件查找時的錯誤結(jié)果,諸如“file not found”(文件不存在)、“no read permission”(無讀權(quán)限) 等等
在使用文件緩存的時候,最好 open_file_cache_errors 也開啟,這個命令我們后面馬上會說。
open_file_cache_errors
開啟或者關(guān)閉緩存文件查找的錯誤結(jié)果
open_file_cache_errors?on?|?off;
默認值是 off ,如果確定要使用文件緩存的話,最好把它也打開。
open_file_cache_min_uses
設(shè)置在由 open_file_cache 指令的 inactive 參數(shù)配置的超時時間內(nèi), 文件應(yīng)該被訪問的最小 number(次數(shù)) 。
open_file_cache_min_uses?number;
如果訪問次數(shù)大于等于此值,文件描述符會保留在緩存中,否則從緩存中刪除。
open_file_cache_valid
設(shè)置檢查 open_file_cache 緩存的元素的時間間隔。
open_file_cache_valid?time;
默認 60s 檢查一次。
配置測試
首先使用 strace 命令追蹤一下當(dāng)前 Nginx 的 Worker 進程,為了方便測試,咱們可以把 worker_processes 設(shè)置為 1 ,只啟動一個工作進程。然后隨便請求一個 URL 就會出現(xiàn)下面的內(nèi)容。
[root@localhost?html]#?strace?-p?2365
strace:?Process?2365?attached
epoll_wait(15,?[{EPOLLIN,?{u32=2915635216,?u64=140495590854672}}],?512,?-1)?=?1
accept4(8,?{sa_family=AF_INET,?sin_port=htons(54245),?sin_addr=inet_addr("192.168.56.1")},?[112->16],?SOCK_NONBLOCK)?=?5
epoll_ctl(15,?EPOLL_CTL_ADD,?5,?{EPOLLIN|EPOLLRDHUP|EPOLLET,?{u32=2915635912,?u64=140495590855368}})?=?0
epoll_wait(15,?[{EPOLLIN,?{u32=2915635912,?u64=140495590855368}}],?512,?60000)?=?1
recvfrom(5,?"GET?/aaa?HTTP/1.1\r\nTEST_UNDERLIN"...,?1024,?0,?NULL,?NULL)?=?224
//?=======注意這里=======
openat(AT_FDCWD,?"/usr/local/nginx/html/aaa",?O_RDONLY|O_NONBLOCK)?=?6
fstat(6,?{st_mode=S_IFDIR|0755,?st_size=24,?...})?=?0
close(6)????????????????????????????????=?0
//?=======注意這里=======
writev(5,?[{iov_base="HTTP/1.1?301?Moved?Permanently\r\n"...,?iov_len=200},?{iov_base="<html>\r\n<head><title>301?Moved?P"...,?iov_len=116},?{iov_base="<hr><center>nginx/1.23.0</center"...,?iov_len=53}],?3)?=?369
write(7,?"192.168.56.1?-?-?[07/Aug/2022:22"...,?102)?=?102
setsockopt(5,?SOL_TCP,?TCP_NODELAY,?[1],?4)?=?0
epoll_wait(15,?[{EPOLLIN,?{u32=2915635912,?u64=140495590855368}}],?512,?65000)?=?1
recvfrom(5,?"GET?/aaa/?HTTP/1.1\r\nTEST_UNDERLI"...,?1024,?0,?NULL,?NULL)?=?260
stat("/usr/local/nginx/html/aaa/index.html",?{st_mode=S_IFREG|0644,?st_size=10,?...})?=?0
//?=======注意這里=======
openat(AT_FDCWD,?"/usr/local/nginx/html/aaa/index.html",?O_RDONLY|O_NONBLOCK)?=?6
fstat(6,?{st_mode=S_IFREG|0644,?st_size=10,?...})?=?0
//?=======注意這里=======
writev(5,?[{iov_base="HTTP/1.1?200?OK\r\nServer:?nginx/1"...,?iov_len=235}],?1)?=?235
sendfile(5,?6,?[0]?=>?[10],?10)?????????=?10
write(7,?"192.168.56.1?-?-?[07/Aug/2022:22"...,?125)?=?125
close(6)????????????????????????????????=?0
epoll_wait(15,
主要需要看的就是上面注釋中的部分,有三個操作,分別是 openat、fstat 和 close 操作,分別對應(yīng)著打開文件句柄、獲取文件統(tǒng)計信息以及關(guān)閉文件句柄這三個操作。目前的情況下,多次訪問,還是一樣的結(jié)果,每次都會有這三個步驟。
接下來,我們就簡單配置下文件緩存,直接使用官方文檔中提供的示例配置。這幾個命令可以配置在 http、server、location 中,我們就簡單地在 server 中進行配置吧。
server?{
??...
??open_file_cache??????????max=1000?inactive=20s;
??open_file_cache_valid????30s;
??open_file_cache_min_uses?2;
??open_file_cache_errors???on;
??...
}
然后再次訪問,第一次還是會正常出現(xiàn) openat、fstat 和 close 這三個系統(tǒng)函數(shù)的調(diào)用。但是之后再次訪問,就會發(fā)現(xiàn)這三個系統(tǒng)調(diào)用不見了。
[root@localhost?html]#?strace?-p?2423
strace:?Process?2423?attached
epoll_wait(15,?[{EPOLLIN,?{u32=2915635216,?u64=140495590854672}}],?512,?-1)?=?1
accept4(8,?{sa_family=AF_INET,?sin_port=htons(65255),?sin_addr=inet_addr("192.168.56.1")},?[112->16],?SOCK_NONBLOCK)?=?7
epoll_ctl(15,?EPOLL_CTL_ADD,?7,?{EPOLLIN|EPOLLRDHUP|EPOLLET,?{u32=2915635912,?u64=140495590855368}})?=?0
epoll_wait(15,?[{EPOLLIN,?{u32=2915635912,?u64=140495590855368}}],?512,?60000)?=?1
recvfrom(7,?"GET?/aaa?HTTP/1.1\r\nTEST_UNDERLIN"...,?1024,?0,?NULL,?NULL)?=?224
//?=======注意這里=======
openat(AT_FDCWD,?"/usr/local/nginx/html/aaa",?O_RDONLY|O_NONBLOCK)?=?12
fstat(12,?{st_mode=S_IFDIR|0755,?st_size=24,?...})?=?0
close(12)???????????????????????????????=?0
//?=======注意這里=======
writev(7,?[{iov_base="HTTP/1.1?301?Moved?Permanently\r\n"...,?iov_len=200},?{iov_base="<html>\r\n<head><title>301?Moved?P"...,?iov_len=116},?{iov_base="<hr><center>nginx/1.23.0</center"...,?iov_len=53}],?3)?=?369
write(5,?"192.168.56.1?-?-?[07/Aug/2022:23"...,?102)?=?102
setsockopt(7,?SOL_TCP,?TCP_NODELAY,?[1],?4)?=?0
epoll_wait(15,?[{EPOLLIN,?{u32=2915635912,?u64=140495590855368}}],?512,?65000)?=?1
recvfrom(7,?"GET?/aaa/?HTTP/1.1\r\nTEST_UNDERLI"...,?1024,?0,?NULL,?NULL)?=?260
//?=======注意這里=======
openat(AT_FDCWD,?"/usr/local/nginx/html/aaa/index.html",?O_RDONLY|O_NONBLOCK)?=?12
fstat(12,?{st_mode=S_IFREG|0644,?st_size=10,?...})?=?0
//?=======注意這里=======
writev(7,?[{iov_base="HTTP/1.1?200?OK\r\nServer:?nginx/1"...,?iov_len=235}],?1)?=?235
sendfile(7,?12,?[0]?=>?[10],?10)????????=?10
write(5,?"192.168.56.1?-?-?[07/Aug/2022:23"...,?125)?=?125
//?這里開始是后續(xù)訪問
epoll_wait(15,?[{EPOLLIN,?{u32=2915635912,?u64=140495590855368}}],?512,?65000)?=?1
recvfrom(7,?"GET?/aaa?HTTP/1.1\r\nTEST_UNDERLIN"...,?1024,?0,?NULL,?NULL)?=?224
writev(7,?[{iov_base="HTTP/1.1?301?Moved?Permanently\r\n"...,?iov_len=200},?{iov_base="<html>\r\n<head><title>301?Moved?P"...,?iov_len=116},?{iov_base="<hr><center>nginx/1.23.0</center"...,?iov_len=53}],?3)?=?369
write(5,?"192.168.56.1?-?-?[07/Aug/2022:23"...,?102)?=?102
epoll_wait(15,?[{EPOLLIN,?{u32=2915635912,?u64=140495590855368}}],?512,?65000)?=?1
recvfrom(7,?"GET?/aaa/?HTTP/1.1\r\nTEST_UNDERLI"...,?1024,?0,?NULL,?NULL)?=?260
writev(7,?[{iov_base="HTTP/1.1?200?OK\r\nServer:?nginx/1"...,?iov_len=235}],?1)?=?235
sendfile(7,?12,?[0]?=>?[10],?10)????????=?10
write(5,?"192.168.56.1?-?-?[07/Aug/2022:23"...,?125)?=?125
epoll_wait(15,
很明顯,這就是文件緩存在起作用。減少了文件相關(guān)的系統(tǒng)調(diào)用讀取的次數(shù)。為什么我們上面訪問的內(nèi)容會有兩遍請求呢?我訪問的是 /aaa 目錄,直接訪問目錄會找這個目錄下面的 index.html 文件,因此有一次 301 跳轉(zhuǎn)。你也可以直接訪問 /aaa/index.html 就會看得更清楚一些了。
這一套文件緩存不會緩存文件的具體內(nèi)容,而只是操作符句柄及文件的一些統(tǒng)計信息,Nginx 雖然已經(jīng)對靜態(tài)內(nèi)容做過優(yōu)化。但在高流量網(wǎng)站的情況下,仍然可以使用 open_file_cache 進一步提高性能,減少系統(tǒng)調(diào)用。通過擴大這個緩存的容量可以提高線上的實際命中率。但是緩存容量并不是越大越好,比如當(dāng)達到 20000 個元素的容量時,共享內(nèi)存的鎖就成了瓶頸。所以,可以在確定訪問頻次非常高的靜態(tài)文件 location 或者 server 上開啟這一套文件緩存,數(shù)量也不用太多,可以讓性能有更進一步的提升。
PHP-FPM 或者反向代理之類的和這個文件緩存就沒啥關(guān)系了,PHP-FPM 走的是 socket 句柄,通過連接 PHP-FPM 進行操作,而打開 php 文件的是 PHP-FPM ,不是 Nginx 。
sendfile
這一塊又要牽涉到操作系統(tǒng)了。在操作系統(tǒng)編程中,sendfile() 是系統(tǒng)調(diào)用,而 read() 這種是函數(shù)調(diào)用,因此,使用 sendfile() 對于文件讀取來說會帶來性能的提升。
讀取發(fā)送文件的時候,使用了 sendfile() 那么 Nginx 就會直接向系統(tǒng)內(nèi)核發(fā)送指令,然后發(fā)送文件也是系統(tǒng)內(nèi)核直接完成,只有一次復(fù)制操作,實現(xiàn)了異步網(wǎng)絡(luò) IO 的形式。而傳統(tǒng)情況則是從磁盤中以流的形式加載文件,然后再將文件流復(fù)制到系統(tǒng)內(nèi)核中,內(nèi)核再發(fā)送。區(qū)別就在這里。其實就是說,傳統(tǒng)方式在讀寫文件時,會從硬盤到系統(tǒng)空間,再到用戶空間這樣走一圈,而使用 sendfile() 則只在系統(tǒng)空間,不用走到用戶空間,從而實現(xiàn)零拷貝,減少空間切換的消耗。
當(dāng)然,sendfile 只對靜態(tài)網(wǎng)站有用,也就是確實需要進行文件讀寫發(fā)送的。如果是動態(tài)網(wǎng)站,比如 FastCGI 或反向代理的,對接的實際上是 socket 接口,真正的文件處理是在動態(tài)語言中進行的,比如 PHP 的模板文件加載等。因此,它只對靜態(tài)頁面或文件有性能提升的效果。
sendfile
開啟或關(guān)閉使用?sendfile()
?調(diào)用。
sendfile?on?|?off;
現(xiàn)在默認就是打開的,從 nginx 0.8.12 和 FreeBSD 5.2.1 開始,可以使用 aio 預(yù)加載 sendfile的數(shù)據(jù),Linux 沒有哦。
send_lowat
如果設(shè)置成非 0 值,Nginx 將嘗試最小化向客戶端發(fā)送數(shù)據(jù)的次數(shù)。
send_lowat?size;
默認值是 0 ,它是通過將 kqueue 方法的 NOTE_LOWAT 標(biāo)志, 或者將套接字的 SO_SNDLOWA T屬性設(shè)置成指定的 size 實現(xiàn)的。這條指令在Linux、Solaris和Windows操作系統(tǒng)無效。
sendfile_max_chunk
設(shè)置為非0值時,可以限制在一次 sendfile() 調(diào)用時傳輸?shù)臄?shù)據(jù)量。
sendfile_max_chunk?size;
最新的版本默認 2m,如果不進行限制,一個快速的連接可能會霸占整個worker進程的所有資源。在版本1.21.4之前,默認情況下是 0 ,沒有限制。
總結(jié)
好吧好吧,今天的內(nèi)容有點水,因為我確實不知道 DirectIO 和 sendfile 在 Nginx 中要怎么測試。如果有了解過的小伙伴記得評論留言哦。不過通常來說,我們會在剛安裝好的 Nginx 配置文件中看到 sendfile 是 on 的狀態(tài),一般來說,不懂的就別瞎配了,咱惹不起還躲不起嘛,其它的配置讓它們就走默認好了。還是那句話,留個印象,將來如果有用到的時候,能夠想起來并且查資料能有個方向就夠了。畢竟,現(xiàn)在的水平只到這里,誰知道將來我們能不能成為大神呢,要是真成了,再回來好好補上唄!
參考文檔:文章來源:http://www.zghlxwxcb.cn/news/detail-608769.html
http://nginx.org/en/docs/http/ngx_http_core_module.html文章來源地址http://www.zghlxwxcb.cn/news/detail-608769.html
到了這里,關(guān)于【Nginx11】Nginx學(xué)習(xí):HTTP核心模塊(八)文件處理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!