我已經(jīng)談過一些關(guān)于Nginx的常見問題; 其中有一些是關(guān)于如何優(yōu)化Nginx. 很多Nginx新用戶是從Apache遷移過來的涵但,因些他們過去常常調(diào)整配置和執(zhí)行魔術(shù)操作來確保服務(wù)器高效運(yùn)行.
我有一些壞消息要告訴你, 你不能像Apache一樣優(yōu)化Nginx.它沒有魔術(shù)配置來減半負(fù)載或是讓PHP運(yùn)行速度加快一倍. 高興的是, Nginx已經(jīng)優(yōu)化的非常好了. 當(dāng)你決定使用Nginx并用apt-get,yum或是make命令安裝的時(shí)候它就已經(jīng)進(jìn)行了最佳優(yōu)化. (注意那些庫經(jīng)常過期,Wiki的安裝頁面上通常有最新的庫)
就是說,很多影響Nginx行為的參數(shù)其默認(rèn)值并不是完全適合高并發(fā)的情況. 我們也要考慮Nginx運(yùn)行所在的平臺(tái),優(yōu)化我們的操作系統(tǒng)當(dāng)有一些限制的時(shí)候.
總的來說,我們無法優(yōu)化單個(gè)連接的負(fù)載時(shí)間,但是我們可以確保Nginx的高并發(fā)處理環(huán)境.當(dāng)然, 對(duì)于高并發(fā)我指的是每秒數(shù)百個(gè)請(qǐng)求連接系奉,大多數(shù)人不需要了解這些.假如你太好奇或是想知道那就繼續(xù)讀吧.
首先,我們需要認(rèn)識(shí)到Nginx幾乎可能需要在所有的平臺(tái)上使用姑廉,MacOS缺亮,Linux,F(xiàn)reeBSD桥言,Solaris萌踱,Windows甚至一些更深?yuàn)W的系統(tǒng)葵礼。他們大部分(這么翻譯好些)實(shí)現(xiàn)了高性能的基于事件的polling方法,不幸的是Nginx的只支持其中4個(gè)系統(tǒng)并鸵。在四個(gè)系統(tǒng)中我傾向于FreeBSD鸳粉,但你不會(huì)看到太大的性能差異,所以選擇的操作系統(tǒng)讓你用起來順手园担,比選擇最優(yōu)化的操作系統(tǒng)更重要(參考舟的第一段翻譯的很好)
我想你一定猜到了windows不在其中. Windows上的nginx確實(shí)沒有什么理由讓你值得使用. Windows有自己的一套處理事件polling. 所以nginx的作者選擇了不支持. 因此默認(rèn)的還是使用select() 這種不是很高效而且性能會(huì)下降很多的方式.
第二個(gè)最大的限制, 也是大多數(shù)人會(huì)遇到的問題是和操作系統(tǒng)相關(guān)的. 打開一個(gè)shell窗口, 使用su命令切換到Nginx的運(yùn)行用戶, 運(yùn)行命令ulimit -a
. 這些值也會(huì)在Nginx在運(yùn)行中對(duì)它進(jìn)行限制. 在許多操作系統(tǒng)中, "open files"的值是相當(dāng)有限的, 在我使用的操作系統(tǒng)中, 它的值是 1024. 如果Nginx在運(yùn)行中操作了這個(gè)限制他會(huì)記錄error log(24: Too many open files) 接著返回一個(gè)操作給客戶端. 當(dāng)然Nginx可以處理的文件數(shù)可以更大你也可以針對(duì)操作系統(tǒng)做一些改動(dòng), 你可以放心的去增加這個(gè)值.
兩種方式可以實(shí)現(xiàn), 你可以通過ulimit設(shè)置os的:"open files", 你還可以通過(nginx)配置 worker_rlimit_nofile 來申明你期望的值.
Nginx 限制
除了注意操作系統(tǒng)的限制, 現(xiàn)在我來深入到Nginx本身,看看一些指令和方法,我們可以用它來調(diào)整Nginx.
Worker Processes(用英文會(huì)更好一些)
worker_process 是Nginx的主干, 一旦主進(jìn)程綁定到指定的IP和端口,就會(huì)使用nginx指定的用戶孵化出子進(jìn)程, 之后他們會(huì)處理所有的工作. Workers 不是多線程的, 所以不能擴(kuò)展它超過CPU的核數(shù). 所以我們應(yīng)該理解設(shè)置多個(gè)(>1)workers的原理, 通常一個(gè)CPU核對(duì)應(yīng)一個(gè)worker. 過猶不及,2-4個(gè)workers會(huì)傷害CPU, 在CPU成為問題之前Nginx會(huì)遇到其他的瓶頸.而通常你只是看到了空閑的進(jìn)程.(這段翻的太爛了希望大家多多改進(jìn))
當(dāng)你正在處理下面這種情況, 你有很多的阻塞(blocking)磁盤IO,這是你可以適當(dāng)增加worker_process的值. 你需要針您的配置進(jìn)行測試,檢查靜態(tài)文件的等待時(shí)間(waiting time), 如果值比較大,可以適當(dāng)?shù)脑黾觲orker_process.
Worker Connections
worker_connections 是個(gè)稍稍有點(diǎn)怪的概念. 我不是很了解這個(gè)指令的目的, 但是它有效的限制了在同一時(shí)間內(nèi)每個(gè)worker可以維護(hù)的連接數(shù). 如果我沒猜錯(cuò)的話, 這個(gè)配置是為了確保在keep-alive配置不正確的情況下, 當(dāng)你使用的端口將要耗盡之時(shí),增加連接數(shù).(這個(gè)翻譯的好難不知道是否正確因?yàn)樽髡咭彩莊orced to guess 我也只能被逼去猜了望指正)
默認(rèn)的值是1024. 我們假設(shè)一個(gè)李蘭奇一般情況下打開2個(gè)連接來通過管道獲取網(wǎng)站資源,也就是最多可以同時(shí)處理512個(gè)用戶的請(qǐng)求.聽起來實(shí)在是太少了,但是我們?cè)谙胍幌履J(rèn)的keepalive-timeout是65(在默認(rèn)配置文件里面提供了65這個(gè)值, 如果沒有設(shè)置該值,默認(rèn)值是75,請(qǐng)參考wiki keepalive_timeout),也就是說我們實(shí)際上每秒只能處理8個(gè)連接. 顯然這個(gè)值高于許多人期望的(我沒覺得高呵呵), 尤其是考慮到我們通常會(huì)設(shè)置2-4個(gè)workers. 但是對(duì)于流量較大的網(wǎng)站 使用keep-alive是值得的.
此外届谈,我們還必須考慮反向代理, 這將打開一個(gè)額外的連接到后臺(tái),但是弯汰,自Nginx的不支持持久連接到后臺(tái)艰山,這不是太大的問題,除非你有長時(shí)間運(yùn)行的后臺(tái)進(jìn)程.
所有關(guān)于worker連接的配置應(yīng)該是相當(dāng)清楚的咏闪,如果你流量增加了曙搬,你要相應(yīng)的增加worker連接的數(shù)量。 2048對(duì)于大多數(shù)人來說應(yīng)該是滿足了,但老實(shí)說汤踏,如果你的流量增長了,那么對(duì)于workers的數(shù)量值應(yīng)該是多少應(yīng)該是很清楚的.
CPU 親和力
設(shè)置CPU的親和力织鲸,基本上意味著你告訴每個(gè)程序使用的CPU核心舔腾,而他們將只使用這個(gè)CPU核心溪胶。關(guān)于這一條,我不想說很多稳诚,但你要知道哗脖,如果你準(zhǔn)備這樣做,則必須非常小心扳还。 要知道才避,你操作系統(tǒng)的 CPU 調(diào)度器處理負(fù)載均衡的能力要遠(yuǎn)遠(yuǎn)超過你。當(dāng)然氨距,如果你認(rèn)為你的 CPU 負(fù)載均衡有問題桑逝,在調(diào)度層面上優(yōu)化它,可能的話找一個(gè)替代的調(diào)度器俏让。除非你知道你在做什么楞遏,否則不要碰這個(gè)。
Keep Alive
keep_alive 是 HTTP的一個(gè)特性, 它允許客戶端維護(hù)與服務(wù)器已經(jīng)創(chuàng)建的連接進(jìn)行一批請(qǐng)求的處理直到指定的超時(shí)時(shí)間到達(dá). 這個(gè)實(shí)際上不會(huì)在很大程度上改變我們的Nginxserver的性能, 因?yàn)镹ginx能夠很好的處理空閑的連接. Nginx的作者聲稱10,000個(gè)空閑的連接智慧使用2.5兆內(nèi)存(unbelievable), 我個(gè)人的使用來說這個(gè)值也是靠譜的.
我在這篇性能文章里面提到這個(gè)原因非常簡單. 對(duì)于最終用戶來說keep alive對(duì)加載時(shí)間有著巨大的影響. 這是最重要的指標(biāo)之一也是我們不斷優(yōu)化的原因.如果你的網(wǎng)站對(duì)用戶來說感覺加載起來很快,他們就會(huì)很開心. Amazon和一些其他的大型在線零售商做過許多類似的研究表明, 網(wǎng)站的加載時(shí)間和網(wǎng)站訂單的完成有著直接的關(guān)系.
為什么keep alive有著如此巨大的影響, 應(yīng)該是顯而易見的, 那就是你避免為所有的HTTP請(qǐng)求創(chuàng)建各自的連接, 這是非常低效的. 也許你不需要把keepalive-timeout設(shè)置為65, 但是10-20應(yīng)該是比較通用的選擇,正如上面一段所說, Nginx會(huì)很好的處理這方面.
tcp_nodelay 和 tcp_nopush
這兩個(gè)指令也許是最難理解的nginx配置, 他們對(duì)于nginx的影響在網(wǎng)絡(luò)的較低層. 你可以簡單的認(rèn)為這些指令決定了操作系統(tǒng)如何處理網(wǎng)絡(luò)緩存和他們何時(shí)將這些緩存輸出到最終用戶(客戶端). 我只能建議大家如果你之前不了解這些概念你最好不要?jiǎng)铀? 他們不會(huì)顯著的改善或者改變性能, 所以最好使用他們的默認(rèn)值.
硬件限制
因?yàn)槲覀円幚韓ginx帶來的所有可能的限制, 所以我們現(xiàn)在需要弄清楚如何有效的利用我們的服務(wù)器.為了做到這點(diǎn)我們需要看一下硬件層面的東西,由于大部分服務(wù)器瓶頸都會(huì)發(fā)生在這里.
一般服務(wù)器主要還有3個(gè)方面的瓶頸. CPU,內(nèi)存和IO. Nginx在CPU的利用方面是非常高效的, 所以我會(huì)坦白的告訴你這不會(huì)成為瓶頸. 同樣nginx在使用內(nèi)存方面也是很高效的,這也不會(huì)成為瓶頸. 現(xiàn)在只剩下IO這個(gè)服務(wù)器瓶頸的罪魁禍?zhǔn)琢?(搞得像找罪犯一樣)
如果你經(jīng)常使用服務(wù)器首昔,那么你可能經(jīng)歷過這樣認(rèn)識(shí)寡喝。硬盤驅(qū)動(dòng)器是真的,真的很慢勒奇。從硬盤驅(qū)動(dòng)器讀取可能是對(duì)服務(wù)器最昂貴的操作. 所以自然得出的結(jié)論是预鬓,為了避免IO瓶頸, 我們需要大量的減少nginx對(duì)硬盤驅(qū)動(dòng)器的讀寫.
要做到這一點(diǎn),我們可以通過修改Nginx的行為赊颠,以減少磁盤寫操作格二,以及確保對(duì)nginx的內(nèi)存限制劈彪,允許它避免磁盤訪問。
Access Logs
默認(rèn)情況下蟋定,Nginx的每個(gè)請(qǐng)求都會(huì)記錄在磁盤上的日志文件中粉臊,你可以使用這個(gè)方法進(jìn)行統(tǒng)計(jì),安全問題檢查等, 帶著這會(huì)在一定程度上帶來IO使用成本. 如果你不打算用這些訪問日志來做一些檢查或其他用途, 你可以直接關(guān)閉它以避免對(duì)磁盤寫操作, 但是如果你需要訪問日志,你可以考慮保存日志到內(nèi)存中.這將會(huì)比直接寫到磁盤上快很多,并且明顯減少IO的使用.
如果你只打算使用訪問日志進(jìn)行統(tǒng)計(jì),你可以考慮使用其他的比如google analytics來取代(ga和access log還是有區(qū)別的 不能簡單的取代哦),或者你只記錄訪問請(qǐng)求的部分信息而不是全部.
Error Logs
我內(nèi)心小小的掙扎了一把,我是否要在這里闡述這個(gè)error log 指令呢,因?yàn)橐苍S你根本不希望關(guān)閉error log, 特別是考慮到實(shí)際應(yīng)用中錯(cuò)誤日志的量會(huì)很少. 但是考慮到這里指令有一個(gè)小小的地方需要引起大家注意, 錯(cuò)誤日志的等級(jí)參數(shù)你是可以指定的, 如果你指定的太低了他會(huì)記錄404錯(cuò)誤甚至是debug信息. 在實(shí)際的應(yīng)用中可以將它設(shè)置為warn級(jí)別,將會(huì)是綽綽有余的并且能降低IO.
Open File Cache
** **從文件系統(tǒng)中讀取文件由2部分組成,打開和關(guān)閉文件. 考慮到這是一個(gè)有阻塞的操作,因此不要忽略這部分. 因此, 對(duì)于我們來說緩存打開文件的描述符是非常好的,這就是open_file_cache指令的由來. 鏈接的wiki地址里對(duì)于使用和配置它有著非常好的說明, 所以我建議你去拜讀一下.
Buffers
配置Nginx緩存的大小是一個(gè)非常重要的事情. 如果緩存大小設(shè)置的太小, Nginx將不得不把上游(用英文upsteams會(huì)更好)的相應(yīng)結(jié)果存放到臨時(shí)的緩存文件里面,這將會(huì)同時(shí)增加IO的讀寫操作, 而且流量越大問題越多.
client_body_buffer_size指令用來指定處理客戶端請(qǐng)求的緩沖區(qū)大小, 這個(gè)代表了訪問請(qǐng)求的body. 這是用來處理POST的數(shù)據(jù),也就是通過提交表單,文件上傳等請(qǐng)求的數(shù)據(jù). 如果你需要處理很多大的POST請(qǐng)求的,你必須確保緩存區(qū)要設(shè)置的足夠大.
fastcgi_buffers 和 proxy_buffers 指令用來處理上流(upstream)的響應(yīng)結(jié)果, 也就是PHP Apache等.它的概念其實(shí)和上面提到的差不多, 如果緩沖區(qū)不足夠大數(shù)據(jù)將在返回給用戶使用之前被保存到磁盤上. 注意Nginx將這個(gè)buffer數(shù)據(jù)同步的傳輸給客戶端之前,有一個(gè)緩存上限, 保存到磁盤也同樣受限. 這個(gè)上線是通過fastcgi_max_temp_file_size和proxy_max_temp_file_size來設(shè)置的. 另外對(duì)于代理的連接你也可以通過把proxy_buffering設(shè)置成off來徹底的關(guān)閉緩存.(通常這不是一個(gè)好辦法).
徹底移除磁盤IO
最好的減少磁盤IO的方法無疑是不使用磁盤, 如果你的的應(yīng)用只有少量的數(shù)據(jù)傳輸,你可以將數(shù)據(jù)都放入內(nèi)存,這樣就可以徹底不用考慮磁盤IO的阻塞了. 當(dāng)然默認(rèn)情況下你的操作系統(tǒng)也會(huì)緩存頻繁訪問的磁盤扇區(qū), 所以內(nèi)存越大磁盤的IO就會(huì)用到的越少. 這就意味著你可以通過增加內(nèi)存來解決IO的瓶頸. 數(shù)據(jù)量越多,需要的內(nèi)存越大.
網(wǎng)絡(luò)IO
為了好玩,我們假設(shè)你有了足夠大的內(nèi)存來緩存你的所有數(shù)據(jù). 這意味著理論上你的IO讀速度達(dá)到了3-6gbps. 但是你沒有那么快的網(wǎng)絡(luò)通道. 不幸的是,我們可以優(yōu)化的網(wǎng)絡(luò)IO是有限的,我們要通過網(wǎng)絡(luò)傳輸數(shù)據(jù),所以還將受制于網(wǎng)絡(luò)IO. 唯一真正有效的方法是盡量減少數(shù)據(jù)量或壓縮驶兜。
幸運(yùn)的是Nginx提供了gzip模塊, 它可以使我們?cè)趯?shù)據(jù)傳輸給客戶端之前壓縮它, 這將大大減少數(shù)據(jù)的大小. 一般來說 gzip_comp_level的值不會(huì)在性能方面有多大的差別,設(shè)為為4-5即可. 一味的增加它是沒有意義的只是浪費(fèi)的CPU的周期. 你也可以通過一些javascript和css縮小工具來減少傳輸文件大小. 但這些不是和Nginx很相關(guān)所以我相信你通過google可以獲取更多的相關(guān)信息.