大家好,分享即關(guān)愛,我們很樂意和你分享一些新的知識(shí),我們準(zhǔn)備了一個(gè) Nginx 的教程,分為三個(gè)系列,如果你對(duì) Nginx 有所耳聞,或者想增進(jìn) Nginx 方面的經(jīng)驗(yàn)和理解,那么恭喜你來對(duì)地方了。
我們會(huì)告訴你 Nginx 如何工作及其背后的理念,還有如何優(yōu)化以加快應(yīng)用的性能,如何安裝啟動(dòng)和保持運(yùn)行。
這個(gè)教程有三個(gè)部分:
基本概念 —— 這部分需要去了解 Nginx 的一些指令和使用場(chǎng)景,繼承模型,以及 Nginx 如何選擇 server 塊,location 的順序。
性能 —— 介紹改善 Nginx 速度的方法和技巧,我們會(huì)在這里談及 gzip 壓縮,緩存,buffer 和超時(shí)。
SSL 安裝 —— 如何配置服務(wù)器使用 HTTPS
創(chuàng)建這個(gè)系列,我們希望,一是作為參考書,可以通過快速查找到相關(guān)問題(比如 gzip 壓縮,SSL 等)的解決方式,也可以直接通讀全文。為了獲得更好的學(xué)習(xí)效果,我們建議你在本機(jī)安裝 Nginx 并且嘗試進(jìn)行實(shí)踐。
tcp_nodelay,tcp_nopush和sendfile
tcp_nodelay
在 TCP 發(fā)展早期,工程師需要面對(duì)流量沖突和堵塞的問題,其中涌現(xiàn)了大批的解決方案,其中之一是由 JohnNagle 提出的算法。
Nagle 的算法旨在防止通訊被大量的小包淹沒。該理論不涉及全尺寸 tcp 包(最大報(bào)文長(zhǎng)度,簡(jiǎn)稱 MSS)的處理。只針對(duì)比 MSS 小的包,只有當(dāng)接收方成功地將以前的包(ACK)的所有確認(rèn)發(fā)送回來時(shí),這些包才會(huì)被發(fā)送。在等待期間,發(fā)送方可以緩沖更多的數(shù)據(jù)之后再發(fā)送。
ifpackage.size>=MSS.size
send(package)
elsifacks.all_received?
send(package)
else
#acumulatedata
end
與此同時(shí),誕生了另一個(gè)理論,延時(shí) ACK
在 TCP 通訊中,在發(fā)送數(shù)據(jù)后,需要接收回應(yīng)包(ACK)來確認(rèn)數(shù)據(jù)被成功傳達(dá)。
延時(shí) ACK 旨在解決線路被大量的 ACK 包擁堵的狀況。為了減少 ACK 包的數(shù)量,接收者等待需要回傳的數(shù)據(jù)加上 ACK 包回傳給發(fā)送方,如果沒有數(shù)據(jù)需要回傳,必須在至少每 2 個(gè) MSS,或每 200 至 500 毫秒內(nèi)發(fā)送 ACK(以防我們不再收到包)。
ifpackages.any?
send
elsiflast_ack_send_more_than_2MSS_ago?||200_ms_timer.finished?
send
else
#wait
end
正如你可能在一開始就注意到的那樣 —— 這可能會(huì)導(dǎo)致在持久連接上的一些暫時(shí)的死鎖。讓我們重現(xiàn)它!
假設(shè):
初始擁塞窗口等于 2。擁塞窗口是另一個(gè) TCP 機(jī)制的一部分,稱為慢啟動(dòng)。細(xì)節(jié)現(xiàn)在并不重要,只要記住它限制了一次可以發(fā)送多少個(gè)包。在第一次往返中,我們可以發(fā)送 2 個(gè) MSS 包。在第二次發(fā)送中:4 個(gè) MSS 包,第三次發(fā)送中:8 個(gè)MSS,依此類推。
4 個(gè)已緩存的等待發(fā)送的數(shù)據(jù)包:A, B, C, D
A, B, C是 MSS 包
D 是一個(gè)小包
場(chǎng)景:
由于是初始的擁塞窗口,發(fā)送端被允許傳送兩個(gè)包:A 和 B
接收端在成功獲得這兩個(gè)包之后,發(fā)送一個(gè) ACK
發(fā)件端發(fā)送 C 包。然而,Nagle 卻阻止它發(fā)送 D 包(包長(zhǎng)度太小,等待 C 的ACK)
在接收端,延遲 ACK 使他無法發(fā)送 ACK(每隔 2 個(gè)包或每隔 200 毫秒發(fā)送一次)
在 200ms 之后,接收器發(fā)送 C 包的 ACK
發(fā)送端收到 ACK 并發(fā)送 D 包
在這個(gè)數(shù)據(jù)交換過程中,由于 Nagel 和延遲 ACK 之間的死鎖,引入了 200ms 的延遲。
Nagle 算法是當(dāng)時(shí)真正的救世主,而且目前仍然具有極大的價(jià)值。但在大多數(shù)情況下,我們不會(huì)在我們的網(wǎng)站上使用它,因此可以通過添加 TCP_NODELAY 標(biāo)志來安全地關(guān)閉它。
tcp_nodelay on; # sets TCP_NODELAY flag, used on keep-alive connections
享受這200ms提速吧!
sendfile
正常來說,當(dāng)要發(fā)送一個(gè)文件時(shí)需要下面的步驟:
malloc(3) – 分配一個(gè)本地緩沖區(qū),儲(chǔ)存對(duì)象數(shù)據(jù)。
read(2) – 檢索和復(fù)制對(duì)象到本地緩沖區(qū)。
write(2) – 從本地緩沖區(qū)復(fù)制對(duì)象到 socket 緩沖區(qū)。
這涉及到兩個(gè)上下文切換(讀,寫),并使相同對(duì)象的第二個(gè)副本成為不必要的。正如你所看到的,這不是最佳的方式。值得慶幸的是還有另一個(gè)系統(tǒng)調(diào)用,提升了發(fā)送文件(的效率),它被稱為:sendfile(2)(想不到吧!居然是這名字)。這個(gè)調(diào)用在文件 cache 中檢索一個(gè)對(duì)象,并傳遞指針(不需要復(fù)制整個(gè)對(duì)象),直接傳遞到 socket 描述符,Netflix 表示,使用 sendfile(2) 將網(wǎng)絡(luò)吞吐量從 6Gbps 提高到了 30Gbps。
然而,sendfile(2) 有一些注意事項(xiàng):
不可用于 UNIX sockets(例如:當(dāng)通過你的上游服務(wù)器發(fā)送靜態(tài)文件時(shí))
能否執(zhí)行不同的操作,取決于操作系統(tǒng)
在 nginx 中打開它
sendfile on;
tcp_nopush
tcp_nopush 與 tcp_nodelay 相反。不是為了盡可能快地推送數(shù)據(jù)包,它的目標(biāo)是一次性優(yōu)化數(shù)據(jù)的發(fā)送量。
在發(fā)送給客戶端之前,它將強(qiáng)制等待包達(dá)到最大長(zhǎng)度(MSS)。而且這個(gè)指令只有在 sendfile 開啟時(shí)才起作用。
sendfileon;
tcp_nopushon;
看起來 tcp_nopush 和 tcp_nodelay 是互斥的。但是,如果所有 3 個(gè)指令都開啟了,nginx 會(huì):
確保數(shù)據(jù)包在發(fā)送給客戶之前是已滿的
對(duì)于最后一個(gè)數(shù)據(jù)包,tcp_nopush 將被刪除 —— 允許 TCP 立即發(fā)送,沒有 200ms 的延遲
我應(yīng)該使用多少進(jìn)程?
工作進(jìn)程
worker_process 指令會(huì)指定:應(yīng)該運(yùn)行多少個(gè) worker。默認(rèn)情況下,此值設(shè)置為 1。最安全的設(shè)置是通過傳遞 auto 選項(xiàng)來使用核心數(shù)量。
但由于 Nginx 的架構(gòu),其處理請(qǐng)求的速度非常快 – 我們可能一次不會(huì)使用超過 2-4 個(gè)進(jìn)程(除非你正在托管 Facebook 或在 nginx 內(nèi)部執(zhí)行一些 CPU 密集型的任務(wù))。
worker_process auto;
worker 連接
與 worker_process直接綁定的指令是 worker_connections。它指定一個(gè)工作進(jìn)程可以一次打開多少個(gè)連接。這個(gè)數(shù)目包括所有連接(例如與代理服務(wù)器的連接),而不僅僅是與客戶端的連接。此外,值得記住的是,一個(gè)客戶端可以打開多個(gè)連接,同時(shí)獲取其他資源。
worker_connections 1024;
打開文件數(shù)目限制
在基于 Unix 系統(tǒng)中的“一切都是文件”。這意味著文檔、目錄、管道甚至套接字都是文件。系統(tǒng)對(duì)一個(gè)進(jìn)程可以打開多少文件有一個(gè)限制。要查看該限制:
ulimit-Sn#softlimit
ulimit-Hn#hardlimit
這個(gè)系統(tǒng)限制必須根據(jù) worker_connections 進(jìn)行調(diào)整。任何傳入的連接都會(huì)打開至少一個(gè)文件(通常是兩個(gè)連接套接字以及后端連接套接字或磁盤上的靜態(tài)文件)。所以這個(gè)值等于 worker_connections*2 是安全的。幸運(yùn)的是,Nginx 提供了一個(gè)配置選項(xiàng)來增加這個(gè)系統(tǒng)的值。要使用這個(gè)配置,請(qǐng)?zhí)砑泳哂羞m當(dāng)數(shù)目的 worker_rlimit_nofile 指令并重新加載 nginx。
worker_rlimit_nofile 2048;
配置
worker_processauto;
worker_rlimit_nofile2048;#Changesthelimitonthemaximumnumberofopenfiles(RLIMIT_NOFILE)forworkerprocesses.
worker_connections1024;#Setsthemaximumnumberofsimultaneousconnectionsthatcanbeopenedbyaworkerprocess.
最大連接數(shù)
如上所述,我們可以計(jì)算一次可以處理多少個(gè)并發(fā)連接:
最大連接數(shù)=
worker_processes*worker_connections
(keep_alive_timeout+avg_response_time)*2
keep_alive_timeout (后續(xù)有更多介紹) + avg_response_time 告訴我們:?jiǎn)蝹€(gè)連接持續(xù)了多久。我們也除以 2,通常情況下,你將有一個(gè)客戶端打開 2 個(gè)連接的情況:一個(gè)在 nginx 和客戶端之間,另一個(gè)在 nginx 和上游服務(wù)器之間。
Gzip
啟用 gzip 可以顯著降低響應(yīng)的(報(bào)文)大小,因此,客戶端(網(wǎng)頁(yè))會(huì)顯得更快些。
壓縮級(jí)別
Gzip 有不同的壓縮級(jí)別,1 到 9 級(jí)。遞增這個(gè)級(jí)別將會(huì)減少文件的大小,但也會(huì)增加資源消耗。作為標(biāo)準(zhǔn)我們將這個(gè)數(shù)字(級(jí)別)保持在 3 – 5 級(jí),就像上面說的那樣,它將會(huì)得到較小的節(jié)省,同時(shí)也會(huì)得到更大的 CPU 使用率。
這有個(gè)通過 gzip 的不同的壓縮級(jí)別壓縮文件的例子,0 代表未壓縮文件。
curl -I -H 'Accept-Encoding: gzip,deflate' https://netguru.co/
?du-sh./*
64K./0_gzip
16K./1_gzip
12K./2_gzip
12K./3_gzip
12K./4_gzip
12K./5_gzip
12K./6_gzip
12K./7_gzip
12K./8_gzip
12K./9_gzip
?ls-al
-rw-r--r--1matDobekstaff617113Nov08:460_gzip
-rw-r--r--1matDobekstaff123313Nov08:481_gzip
-rw-r--r--1matDobekstaff121233Nov08:482_gzip
-rw-r--r--1matDobekstaff120033Nov08:483_gzip
-rw-r--r--1matDobekstaff112643Nov08:494_gzip
-rw-r--r--1matDobekstaff111113Nov08:505_gzip
-rw-r--r--1matDobekstaff110973Nov08:506_gzip
-rw-r--r--1matDobekstaff110803Nov08:507_gzip
-rw-r--r--1matDobekstaff110713Nov08:518_gzip
-rw-r--r--1matDobekstaff110053Nov08:519_gzip
gzip_http_version 1.1;
這條指令告訴 nginx 僅在 HTTP 1.1 以上的版本才能使用 gzip。我們?cè)谶@里不涉及 HTTP 1.0,至于 HTTP 1.0 版本,它是不可能既使用 keep-alive 和 gzip 的。因此你必須做出決定:使用 HTTP 1.0 的客戶端要么錯(cuò)過 gzip,要么錯(cuò)過 keep-alive。
配置
gzipon;#enablegzip
gzip_http_version1.1;#turnongzipforhttp1.1andabove
gzip_disable"msie6";#IE6hadissueswithgzip
gzip_comp_level5;#inccompresionlevel,andCPUusage
gzip_min_length100;#minimalweighttogzipfile
gzip_proxiedany;#enablegzipforproxiedrequests(e.g.CDN)
gzip_buffers168k;#compressionbuffers(ifweexceedthisvalue,diskwillbeusedinsteadofRAM)
gzip_varyon;#addheaderVaryAccept-Encoding(moreonthatinCachingsection)
#definefileswhichshouldbecompressed
gzip_typestext/plain;
gzip_typestext/css;
gzip_typesapplication/javascript;
gzip_typesapplication/json;
gzip_typesapplication/vnd.ms-fontobject;
gzip_typesapplication/x-font-ttf;
gzip_typesfont/opentype;
gzip_typesimage/svg+xml;
gzip_typesimage/x-icon;
緩存
緩存是另一回事,它能提升用戶的請(qǐng)求速度。
管理緩存可以僅由 2 個(gè) header 控制:
在 HTTP/1.1 中用Cache-Control管理緩存
Pragma 對(duì)于 HTTP/1.0 客戶端的向后兼容性
緩存本身可以分為兩類:公共緩存和私有緩存。公共緩存是被多個(gè)用戶共同使用的。專用緩存專用于單個(gè)用戶。我們可以很容易地區(qū)分,應(yīng)該使用哪種緩存:
add_headerCache-Controlpublic;
add_headerPragmapublic;
對(duì)于標(biāo)準(zhǔn)資源,我們想保存1個(gè)月:
location~*\.(jpg|jpeg|png|gif|ico|css|js)${
expires1M;
add_headerCache-Controlpublic;
add_headerPragmapublic;
}
上面的配置似乎足夠了。然而,使用公共緩存時(shí)有一個(gè)注意事項(xiàng)。
讓我們看看如果將我們的資源存儲(chǔ)在公共緩存(如 CDN)中,URI 將是唯一的標(biāo)識(shí)符。在這種情況下,我們認(rèn)為 gzip 是開啟的。
有2個(gè)瀏覽器:
舊的,不支持 gzip
新的,支持 gzip
舊的瀏覽器給 CDN 發(fā)送了一個(gè) netguru.co/style 請(qǐng)求。但是 CDN 也沒有這個(gè)資源,它將會(huì)給我們的服務(wù)器發(fā)送請(qǐng)求,并且返回未經(jīng)壓縮的響應(yīng)。CDN 在哈希里存儲(chǔ)文件(為以后使用):
{
...
netguru.co/styles.css=>FILE("/sites/netguru/style.css")
...
}
然后將其返回給客戶端。
現(xiàn)在,新的瀏覽器發(fā)送相同的請(qǐng)求到 CDN,請(qǐng)求 netguru.co/style.css,獲取 gzip 打包的資源。由于 CDN 僅通過 URI 標(biāo)識(shí)資源,它將為新瀏覽器返回一樣的未壓縮資源。新的瀏覽器將嘗試提取未打包的文件,但是將獲得無用的東西。
如果我們能夠告訴公共緩存是怎樣進(jìn)行 URI 和編碼的資源識(shí)別,我們就可以避免這個(gè)問題。
{
...
(netguru.co/styles.css,gzip)=>FILE("/sites/netguru/style.css.gzip")
(netguru.co/styles.css,text/css)=>FILE("/sites/netguru/style.css")
...
}
這正是 Vary Accept-Encoding: 完成的。它告訴公共緩存,可以通過 URI 和 Accept-Encoding header 區(qū)分資源。
所以我們的最終配置如下:
location~*\.(jpg|jpeg|png|gif|ico|css|js)${
expires1M;
add_headerCache-Controlpublic;
add_headerPragmapublic;
add_headerVaryAccept-Encoding;
}
超時(shí)
client_body_timeout 和 client_header_timeout 定義了 nginx 在拋出 408(請(qǐng)求超時(shí))錯(cuò)誤之前應(yīng)該等待客戶端傳輸主體或頭信息的時(shí)間。
send_timeout 設(shè)置向客戶端發(fā)送響應(yīng)的超時(shí)時(shí)間。超時(shí)僅在兩次連續(xù)的寫入操作之間被設(shè)置,而不是用于整個(gè)響應(yīng)的傳輸過程。如果客戶端在給定時(shí)間內(nèi)沒有收到任何內(nèi)容,則連接將被關(guān)閉。
設(shè)置這些值時(shí)要小心,因?yàn)榈却龝r(shí)間過長(zhǎng)會(huì)使你容易受到攻擊者的攻擊,并且等待時(shí)間太短的話會(huì)切斷與速度較慢的客戶端的連接。
#Configuretimeouts
client_body_timeout12;
client_header_timeout12;
send_timeout10;
Buffers
client_body_buffer_size
設(shè)置讀取客戶端請(qǐng)求正文的緩沖區(qū)大小。如果請(qǐng)求主體大于緩沖區(qū),則整個(gè)主體或僅其部分被寫入臨時(shí)文件。對(duì) client_body_buffer_size 而言,設(shè)置 16k 大小在大多數(shù)情況下是足夠的。
這是又一個(gè)可以產(chǎn)生巨大影響的設(shè)置,必須謹(jǐn)慎使用。太小了,則 nginx 會(huì)不斷地使用 I/O 把剩余的部分寫入文件。太大了,則當(dāng)攻擊者可以打開所有連接但你無法在系統(tǒng)上分配足夠緩沖來處理這些連接時(shí),你可能容易受到 DOS 攻擊。
client_header_buffer_size 和 large_client_header_buffers
如果 header 不能跟 client_header_buffer_size 匹配上,就會(huì)使用 large_client_header_buffers。如果請(qǐng)求也不適合 large_client_header_buffers,將給客戶端返回一個(gè)錯(cuò)誤提示。對(duì)于大多數(shù)的請(qǐng)求來說,1KB 的緩存是足夠的。但是,如果一個(gè)包含大量記錄的請(qǐng)求,1KB 是不夠的。
如果請(qǐng)求行的長(zhǎng)度超限,將給客戶端返回一個(gè) 414(請(qǐng)求的 URI 太長(zhǎng))錯(cuò)誤提示。如果請(qǐng)求的 header 長(zhǎng)度超限,將拋出一個(gè) 400(錯(cuò)誤請(qǐng)求)的錯(cuò)誤代碼
client_max_body_size
設(shè)置客戶端請(qǐng)求主體的最大允許范圍,在請(qǐng)求頭字段中指定“內(nèi)容長(zhǎng)度”。如果您希望允許用戶上傳文件,調(diào)整此配置以滿足您的需要。
配置
client_body_buffer_size16K;
client_header_buffer_size1k;
large_client_header_buffers21k;
client_max_body_size8m;
Keep-Alive
HTTP 所依賴的 TCP 協(xié)議需要執(zhí)行三次握手來啟動(dòng)連接。這意味著在服務(wù)器可發(fā)送數(shù)據(jù)(例如圖像)之前,需要在客戶機(jī)和服務(wù)器之間進(jìn)行三次完整的往返。
假設(shè)你從 Warsaw 請(qǐng)求的 /image.jpg,并連接到在柏林最近的服務(wù)器:
Open connection
TCP Handshake:
Warsaw->------------------ synchronize packet(SYN) ----------------->- Berlin
Warsaw-<--------- synchronise-acknowledgement packet(SYN-ACK) ------<- Berlin
Warsaw->------------------- acknowledgement(ACK) ------------------->- Berlin
Data transfer:
Warsaw->---------------------- /image.jpg --------------------------->- Berlin
Warsaw-<--------------------- (image data) --------------------------<- Berlin
Close connection
對(duì)于另一次請(qǐng)求,你將不得不再次執(zhí)行整個(gè)初始化。如果你在短時(shí)間內(nèi)發(fā)送多次請(qǐng)求,這可能會(huì)快速累積起來。這樣的話 keep-alive 使用起來就方便了。在成功響應(yīng)之后,它保持連接空閑給定的時(shí)間段(例如 10 秒)。如果在這段時(shí)間內(nèi)有另一個(gè)請(qǐng)求,現(xiàn)有的連接將被重用,空閑時(shí)間將被刷新。
Nginx 提供了幾個(gè)指令來調(diào)整 keepalive 設(shè)置。這些可以分為兩類:
在客戶端和 nginx 之間 keep-alive
keepalive_disablemsie6;#disableselectedbrowsers.
#Thenumberofrequestsaclientcanmakeoverasinglekeepaliveconnection.Thedefaultis100,butamuchhighervaluecanbeespeciallyusefulfortestingwithaload?generationtool,whichgenerallysendsalargenumberofrequestsfromasingleclient.
keepalive_requests100000;
#Howlonganidlekeepaliveconnectionremainsopen.
keepalive_timeout60;
在 nginx 和上游服務(wù)器之間 keep-alive
upstreambackend{
#Thenumberofidlekeepaliveconnectionstoanupstreamserverthatremainopenforeachworkerprocess
keepalive16;
}
server{
location/http/{
proxy_passhttp://http_backend;
proxy_http_version1.1;
proxy_set_headerConnection"";
}
}
-
Linux
+關(guān)注
關(guān)注
87文章
11509瀏覽量
213709 -
SSL
+關(guān)注
關(guān)注
0文章
130瀏覽量
26199 -
nginx
+關(guān)注
關(guān)注
0文章
171瀏覽量
12588
原文標(biāo)題:Nginx 教程(2):性能
文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
NanoPi NEO - 可靠的Nginx 網(wǎng)絡(luò)服務(wù)器
天線分集技術(shù)的基本概念介紹
USB基本概念及從機(jī)編程方法介紹
Uart協(xié)議(即串口)的基本概念及相關(guān)知識(shí)介紹
主要學(xué)習(xí)下nginx的安裝配置
搭建Keepalived+Lvs+Nginx高可用集群負(fù)載均衡

時(shí)序分析Slew/Transition基本概念介紹

時(shí)序分析基本概念介紹—Timing Arc

評(píng)論