亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

Nginx是一款輕量級(jí)的Web 服務(wù)器/反向代理服務(wù)器及電子郵件(IMAP/POP3)代理服務(wù)器,在BSD-like 協(xié)議下發(fā)行。其特點(diǎn)是占有內(nèi)存少,并發(fā)能力強(qiáng),事實(shí)上nginx的并發(fā)能力確實(shí)在同類型的網(wǎng)頁服務(wù)器中表現(xiàn)較好,中國大陸幾乎所有top級(jí)公司都是nginx用戶:百度、京東、新浪、網(wǎng)易、騰訊、淘寶等。我們先來了解一下它的工作原理。

歡迎關(guān)注筆者,優(yōu)質(zhì)文章都在這里等你。

一. Nginx的模塊與工作原理

Nginx由內(nèi)核和模塊組成,其中,內(nèi)核的設(shè)計(jì)非常微小和簡潔,完成的工作也非常簡單,僅僅通過查找配置文件將客戶端請(qǐng)求映射到一個(gè)location block(location是Nginx配置中的一個(gè)指令,用于URL匹配),而在這個(gè)location中所配置的每個(gè)指令將會(huì)啟動(dòng)不同的模塊去完成相應(yīng)的工作。

Nginx的模塊從結(jié)構(gòu)上分為核心模塊、基礎(chǔ)模塊和第三方模塊:

  • 核心模塊:HTTP模塊、EVENT模塊和MAIL模塊
  • 基礎(chǔ)模塊:HTTP Access模塊、HTTP FastCGI模塊、HTTP Proxy模塊和HTTP Rewrite模塊,
  • 第三方模塊:HTTP Upstream Request Hash模塊、Notice模塊和HTTP Access Key模塊。

用戶根據(jù)自己的需要開發(fā)的模塊都屬于第三方模塊。正是有了這么多模塊的支撐,Nginx的功能才會(huì)如此強(qiáng)大。

Nginx的模塊從功能上分為如下三類。

  • Handlers(處理器模塊)。此類模塊直接處理請(qǐng)求,并進(jìn)行輸出內(nèi)容和修改headers信息等操作。Handlers處理器模塊一般只能有一個(gè)。
  • Filters (過濾器模塊)。此類模塊主要對(duì)其他處理器模塊輸出的內(nèi)容進(jìn)行修改操作,最后由Nginx輸出。
  • Proxies (代理類模塊)。此類模塊是Nginx的HTTP Upstream之類的模塊,這些模塊主要與后端一些服務(wù)比如FastCGI等進(jìn)行交互,實(shí)現(xiàn)服務(wù)代理和負(fù)載均衡等功能。

圖1-1展示了Nginx模塊常規(guī)的HTTP請(qǐng)求和響應(yīng)的過程。

就這六點(diǎn),5分鐘徹底搞清楚nginx工作原理【程序員必備】

 

Nginx本身做的工作實(shí)際很少,當(dāng)它接到一個(gè)HTTP請(qǐng)求時(shí),它僅僅是通過查找配置文件將此次請(qǐng)求映射到一個(gè)location block,而此location中所配置的各個(gè)指令則會(huì)啟動(dòng)不同的模塊去完成工作,因此模塊可以看做Nginx真正的勞動(dòng)工作者。

通常一個(gè)location中的指令會(huì)涉及一個(gè)handler模塊和多個(gè)filter模塊(當(dāng)然,多個(gè)location可以復(fù)用同一個(gè)模塊)。handler模塊負(fù)責(zé)處理請(qǐng)求,完成響應(yīng)內(nèi)容的生成,而filter模塊對(duì)響應(yīng)內(nèi)容進(jìn)行處理。

Nginx的模塊直接被編譯進(jìn)Nginx,因此屬于靜態(tài)編譯方式。啟動(dòng)Nginx后,Nginx的模塊被自動(dòng)加載,不像Apache,首先將模塊編譯為一個(gè)so文件,然后在配置文件中指定是否進(jìn)行加載。在解析配置文件時(shí),Nginx的每個(gè)模塊都有可能去處理某個(gè)請(qǐng)求,但是同一個(gè)處理請(qǐng)求只能由一個(gè)模塊來完成。

二. Nginx的進(jìn)程模型

在工作方式上,Nginx分為單工作進(jìn)程和多工作進(jìn)程兩種模式。在單工作進(jìn)程模式下,除主進(jìn)程外,還有一個(gè)工作進(jìn)程,工作進(jìn)程是單線程的;在多工作進(jìn)程模式下,每個(gè)工作進(jìn)程包含多個(gè)線程。Nginx默認(rèn)為單工作進(jìn)程模式。

Nginx在啟動(dòng)后,會(huì)有一個(gè)master進(jìn)程和多個(gè)worker進(jìn)程。

master進(jìn)程

主要用來管理worker進(jìn)程,包含:接收來自外界的信號(hào),向各worker進(jìn)程發(fā)送信號(hào),監(jiān)控worker進(jìn)程的運(yùn)行狀態(tài),當(dāng)worker進(jìn)程退出后(異常情況下),會(huì)自動(dòng)重新啟動(dòng)新的worker進(jìn)程。

master進(jìn)程充當(dāng)整個(gè)進(jìn)程組與用戶的交互接口,同時(shí)對(duì)進(jìn)程進(jìn)行監(jiān)護(hù)。它不需要處理網(wǎng)絡(luò)事件,不負(fù)責(zé)業(yè)務(wù)的執(zhí)行,只會(huì)通過管理worker進(jìn)程來實(shí)現(xiàn)重啟服務(wù)、平滑升級(jí)、更換日志文件、配置文件實(shí)時(shí)生效等功能。

我們要控制nginx,只需要通過kill向master進(jìn)程發(fā)送信號(hào)就行了。比如kill -HUP pid,則是告訴nginx,從容地重啟nginx,我們一般用這個(gè)信號(hào)來重啟nginx,或重新加載配置,因?yàn)槭菑娜莸刂貑?,因此服?wù)是不中斷的。

master進(jìn)程在接收到HUP信號(hào)后是怎么做的呢?首先master進(jìn)程在接到信號(hào)后,會(huì)先重新加載配置文件,然后再啟動(dòng)新的worker進(jìn)程,并向所有老的worker進(jìn)程發(fā)送信號(hào),告訴他們可以光榮退休了。

新的worker在啟動(dòng)后,就開始接收新的請(qǐng)求,而老的worker在收到來自master的信號(hào)后,就不再接收新的請(qǐng)求,并且在當(dāng)前進(jìn)程中的所有未處理完的請(qǐng)求處理完成后,再退出。當(dāng)然,直接給master進(jìn)程發(fā)送信號(hào),這是比較老的操作方式,nginx在0.8版本之后,引入了一系列命令行參數(shù),來方便我們管理。比如,./nginx -s reload,就是來重啟nginx,./nginx -s stop,就是來停止nginx的運(yùn)行。

如何做到的呢?我們還是拿reload來說,我們看到,執(zhí)行命令時(shí),我們是啟動(dòng)一個(gè)新的nginx進(jìn)程,而新的nginx進(jìn)程在解析到reload參數(shù)后,就知道我們的目的是控制nginx來重新加載配置文件了,它會(huì)向master進(jìn)程發(fā)送信號(hào),然后接下來的動(dòng)作,就和我們直接向master進(jìn)程發(fā)送信號(hào)一樣了。

worker進(jìn)程:

而基本的網(wǎng)絡(luò)事件,則是放在worker進(jìn)程中來處理了。多個(gè)worker進(jìn)程之間是對(duì)等的,他們同等競爭來自客戶端的請(qǐng)求,各進(jìn)程互相之間是獨(dú)立的。一個(gè)請(qǐng)求,只可能在一個(gè)worker進(jìn)程中處理,一個(gè)worker進(jìn)程,不可能處理其它進(jìn)程的請(qǐng)求。worker進(jìn)程的個(gè)數(shù)是可以設(shè)置的,一般我們會(huì)設(shè)置與機(jī)器cpu核數(shù)一致,這里面的原因與nginx的進(jìn)程模型以及事件處理模型是分不開的。

worker進(jìn)程之間是平等的,每個(gè)進(jìn)程,處理請(qǐng)求的機(jī)會(huì)也是一樣的。當(dāng)我們提供80端口的http服務(wù)時(shí),一個(gè)連接請(qǐng)求過來,每個(gè)進(jìn)程都有可能處理這個(gè)連接,怎么做到的呢?首先,每個(gè)worker進(jìn)程都是從master進(jìn)程fork過來,在master進(jìn)程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多個(gè)worker進(jìn)程。

所有worker進(jìn)程的listenfd會(huì)在新連接到來時(shí)變得可讀,為保證只有一個(gè)進(jìn)程處理該連接,所有worker進(jìn)程在注冊(cè)listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個(gè)進(jìn)程注冊(cè)listenfd讀事件,在讀事件里調(diào)用accept接受該連接。

當(dāng)一個(gè)worker進(jìn)程在accept這個(gè)連接之后,就開始讀取請(qǐng)求,解析請(qǐng)求,處理請(qǐng)求,產(chǎn)生數(shù)據(jù)后,再返回給客戶端,最后才斷開連接,這樣一個(gè)完整的請(qǐng)求就是這樣的了。我們可以看到,一個(gè)請(qǐng)求,完全由worker進(jìn)程來處理,而且只在一個(gè)worker進(jìn)程中處理。

worker進(jìn)程之間是平等的,每個(gè)進(jìn)程,處理請(qǐng)求的機(jī)會(huì)也是一樣的。當(dāng)我們提供80端口的http服務(wù)時(shí),一個(gè)連接請(qǐng)求過來,每個(gè)進(jìn)程都有可能處理這個(gè)連接,怎么做到的呢?首先,每個(gè)worker進(jìn)程都是從master進(jìn)程fork過來,在master進(jìn)程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多個(gè)worker進(jìn)程。

所有worker進(jìn)程的listenfd會(huì)在新連接到來時(shí)變得可讀,為保證只有一個(gè)進(jìn)程處理該連接,所有worker進(jìn)程在注冊(cè)listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個(gè)進(jìn)程注冊(cè)listenfd讀事件,在讀事件里調(diào)用accept接受該連接。

當(dāng)一個(gè)worker進(jìn)程在accept這個(gè)連接之后,就開始讀取請(qǐng)求,解析請(qǐng)求,處理請(qǐng)求,產(chǎn)生數(shù)據(jù)后,再返回給客戶端,最后才斷開連接,這樣一個(gè)完整的請(qǐng)求就是這樣的了。我們可以看到,一個(gè)請(qǐng)求,完全由worker進(jìn)程來處理,而且只在一個(gè)worker進(jìn)程中處理。

三. Nginx+FastCGI運(yùn)行原理

1、什么是 FastCGI

FastCGI是一個(gè)可伸縮地、高速地在HTTP server和動(dòng)態(tài)腳本語言間通信的接口。多數(shù)流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等。同時(shí),F(xiàn)astCGI也被許多腳本語言支持,其中就有php。

FastCGI是從CGI發(fā)展改進(jìn)而來的。傳統(tǒng)CGI接口方式的主要缺點(diǎn)是性能很差,因?yàn)槊看蜨TTP服務(wù)器遇到動(dòng)態(tài)程序時(shí)都需要重新啟動(dòng)腳本解析器來執(zhí)行解析,然后將結(jié)果返回給HTTP服務(wù)器。這在處理高并發(fā)訪問時(shí)幾乎是不可用的。另外傳統(tǒng)的CGI接口方式安全性也很差,現(xiàn)在已經(jīng)很少使用了。

FastCGI接口方式采用C/S結(jié)構(gòu),可以將HTTP服務(wù)器和腳本解析服務(wù)器分開,同時(shí)在腳本解析服務(wù)器上啟動(dòng)一個(gè)或者多個(gè)腳本解析守護(hù)進(jìn)程。當(dāng)HTTP服務(wù)器每次遇到動(dòng)態(tài)程序時(shí),可以將其直接交付給FastCGI進(jìn)程來執(zhí)行,然后將得到的結(jié)果返回給瀏覽器。這種方式可以讓HTTP服務(wù)器專一地處理靜態(tài)請(qǐng)求或者將動(dòng)態(tài)腳本服務(wù)器的結(jié)果返回給客戶端,這在很大程度上提高了整個(gè)應(yīng)用系統(tǒng)的性能。

2、Nginx+FastCGI運(yùn)行原理

Nginx不支持對(duì)外部程序的直接調(diào)用或者解析,所有的外部程序(包括PHP)必須通過FastCGI接口來調(diào)用。FastCGI接口在linux下是socket(這個(gè)socket可以是文件socket,也可以是ip socket)。

wrApper:為了調(diào)用CGI程序,還需要一個(gè)FastCGI的wrapper(wrapper可以理解為用于啟動(dòng)另一個(gè)程序的程序),這個(gè)wrapper綁定在某個(gè)固定socket上,如端口或者文件socket。當(dāng)Nginx將CGI請(qǐng)求發(fā)送給這個(gè)socket的時(shí)候,通過FastCGI接口,wrapper接收到請(qǐng)求,然后Fork(派生)出一個(gè)新的線程,這個(gè)線程調(diào)用解釋器或者外部程序處理腳本并讀取返回?cái)?shù)據(jù);接著,wrapper再將返回的數(shù)據(jù)通過FastCGI接口,沿著固定的socket傳遞給Nginx;最后,Nginx將返回的數(shù)據(jù)(html頁面或者圖片)發(fā)送給客戶端。

這就是Nginx+FastCGI的整個(gè)運(yùn)作過程,如圖所示。

就這六點(diǎn),5分鐘徹底搞清楚nginx工作原理【程序員必備】

 

所以,我們首先需要一個(gè)wrapper,這個(gè)wrapper需要完成的工作:

通過調(diào)用fastcgi(庫)的函數(shù)通過socket和ningx通信(讀寫socket是fastcgi內(nèi)部實(shí)現(xiàn)的功能,對(duì)wrapper是非透明的)調(diào)度thread,進(jìn)行fork和kill和application(php)進(jìn)行通信

3、spawn-fcgi與PHP-FPM

FastCGI接口方式在腳本解析服務(wù)器上啟動(dòng)一個(gè)或者多個(gè)守護(hù)進(jìn)程對(duì)動(dòng)態(tài)腳本進(jìn)行解析,這些進(jìn)程就是FastCGI進(jìn)程管理器,或者稱為FastCGI引擎。 spawn-fcgi與PHP-FPM就是支持PHP的兩個(gè)FastCGI進(jìn)程管理器。因此HTTPServer完全解放出來,可以更好地進(jìn)行響應(yīng)和并發(fā)處理。

spawn-fcgi與PHP-FPM的異同:

1)spawn-fcgi是HTTP服務(wù)器lighttpd的一部分,目前已經(jīng)獨(dú)立成為一個(gè)項(xiàng)目,一般與lighttpd配合使用來支持PHP。但是ligttpd的spwan-fcgi在高并發(fā)訪問的時(shí)候,會(huì)出現(xiàn)內(nèi)存泄漏甚至自動(dòng)重啟FastCGI的問題。即:PHP腳本處理器當(dāng)機(jī),這個(gè)時(shí)候如果用戶訪問的話,可能就會(huì)出現(xiàn)白頁(即PHP不能被解析或者出錯(cuò))。

2)Nginx是個(gè)輕量級(jí)的HTTP server,必須借助第三方的FastCGI處理器才可以對(duì)PHP進(jìn)行解析,因此其實(shí)這樣看來nginx是非常靈活的,它可以和任何第三方提供解析的處理器實(shí)現(xiàn)連接從而實(shí)現(xiàn)對(duì)PHP的解析(在nginx.conf中很容易設(shè)置)。nginx也可以使用spwan-fcgi(需要一同安裝lighttpd,但是需要為nginx避開端口,一些較早的blog有這方面安裝的教程),但是由于spawn-fcgi具有上面所述的用戶逐漸發(fā)現(xiàn)的缺陷,現(xiàn)在慢慢減少用nginx+spawn-fcgi組合了。

由于spawn-fcgi的缺陷,現(xiàn)在出現(xiàn)了第三方(目前已經(jīng)加入到PHP core中)的PHP的FastCGI處理器PHP-FPM,它和spawn-fcgi比較起來有如下優(yōu)點(diǎn):

由于它是作為PHP的patch補(bǔ)丁來開發(fā)的,安裝的時(shí)候需要和php源碼一起編譯,也就是說編譯到php core中了,因此在性能方面要優(yōu)秀一些;

同時(shí)它在處理高并發(fā)方面也優(yōu)于spawn-fcgi,至少不會(huì)自動(dòng)重啟fastcgi處理器。因此,推薦使用Nginx+PHP/PHP-FPM這個(gè)組合對(duì)PHP進(jìn)行解析。

相對(duì)Spawn-FCGI,PHP-FPM在CPU和內(nèi)存方面的控制都更勝一籌,而且前者很容易崩潰,必須用crontab進(jìn)行監(jiān)控,而PHP-FPM則沒有這種煩惱。

FastCGI 的主要優(yōu)點(diǎn)是把動(dòng)態(tài)語言和HTTP Server分離開來,所以Nginx與PHP/PHP-FPM經(jīng)常被部署在不同的服務(wù)器上,以分擔(dān)前端Nginx服務(wù)器的壓力,使Nginx專一處理靜態(tài)請(qǐng)求和轉(zhuǎn)發(fā)動(dòng)態(tài)請(qǐng)求,而PHP/PHP-FPM服務(wù)器專一解析PHP動(dòng)態(tài)請(qǐng)求。

4、Nginx+PHP-FPM

PHP-FPM是管理FastCGI的一個(gè)管理器,它作為PHP的插件存在,在安裝PHP要想使用PHP-FPM時(shí)在老php的老版本(php5.3.3之前)就需要把PHP-FPM以補(bǔ)丁的形式安裝到PHP中,而且PHP要與PHP-FPM版本一致,這是必須的)

PHP-FPM其實(shí)是PHP源代碼的一個(gè)補(bǔ)丁,旨在將FastCGI進(jìn)程管理整合進(jìn)PHP包中。必須將它patch到你的PHP源代碼中,在編譯安裝PHP后才可以使用。

PHP5.3.3已經(jīng)集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP進(jìn)程管理方式,可以有效控制內(nèi)存和進(jìn)程、可以平滑重載PHP配置,比spawn-fcgi具有更多優(yōu)點(diǎn),所以被PHP官方收錄了。在./configure的時(shí)候帶 –enable-fpm參數(shù)即可開啟PHP-FPM。

fastcgi已經(jīng)在php5.3.5的core中了,不必在configure時(shí)添加 --enable-fastcgi了。老版本如php5.2的需要加此項(xiàng)。

當(dāng)我們安裝Nginx和PHP-FPM完后,配置信息:

PHP-FPM的默認(rèn)配置php-fpm.conf:

listen_address 127.0.0.1:9000 #這個(gè)表示php的fastcgi進(jìn)程監(jiān)聽的ip地址以及端口

start_servers

min_spare_servers

max_spare_servers

Nginx配置運(yùn)行php: 編輯nginx.conf加入如下語句:

location ~ .php$ {

root html;

fastcgi_pass 127.0.0.1:9000; 指定了fastcgi進(jìn)程偵聽的端口,nginx就是通過這里與php交互的

fastcgi_index index.php;

include fastcgi_params;

fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name;

}

Nginx通過location指令,將所有以php為后綴的文件都交給127.0.0.1:9000來處理,而這里的IP地址和端口就是FastCGI進(jìn)程監(jiān)聽的IP地址和端口。

其整體工作流程:

1)、FastCGI進(jìn)程管理器php-fpm自身初始化,啟動(dòng)主進(jìn)程php-fpm和啟動(dòng)start_servers個(gè)CGI 子進(jìn)程。

主進(jìn)程php-fpm主要是管理fastcgi子進(jìn)程,監(jiān)聽9000端口。

fastcgi子進(jìn)程等待來自Web Server的連接。

2)、當(dāng)客戶端請(qǐng)求到達(dá)Web Server Nginx是時(shí),Nginx通過location指令,將所有以php為后綴的文件都交給127.0.0.1:9000來處理,即Nginx通過location指令,將所有以php為后綴的文件都交給127.0.0.1:9000來處理。

3)FastCGI進(jìn)程管理器PHP-FPM選擇并連接到一個(gè)子進(jìn)程CGI解釋器。Web server將CGI環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到FastCGI子進(jìn)程。

4)、FastCGI子進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一連接返回Web Server。當(dāng)FastCGI子進(jìn)程關(guān)閉連接時(shí),請(qǐng)求便告處理完成。

5)、FastCGI子進(jìn)程接著等待并處理來自FastCGI進(jìn)程管理器(運(yùn)行在 WebServer中)的下一個(gè)連接。

四. Nginx+PHP正確配置

一般web都做統(tǒng)一入口:把PHP請(qǐng)求都發(fā)送到同一個(gè)文件上,然后在此文件里通過解析「REQUEST_URI」實(shí)現(xiàn)路由。

Nginx配置文件分為好多塊,常見的從外到內(nèi)依次是「http」、「server」、「location」等等,缺省的繼承關(guān)系是從外到內(nèi),也就是說內(nèi)層塊會(huì)自動(dòng)獲取外層塊的值作為缺省值。

例如:

server {

listen 80;

server_name foo.com;

root /path;

location / {

index index.html index.htm index.php;

if (!-e $request_filename) {

rewrite . /index.php last;

}

}

location ~ .php$ {

include fastcgi_params;

fastcgi_param SCRIPT_FILENAME /path$fastcgi_script_name;

fastcgi_pass 127.0.0.1:9000;

fastcgi_index index.php;

}

}

1) 不應(yīng)該在location 模塊定義index

一旦未來需要加入新的「location」,必然會(huì)出現(xiàn)重復(fù)定義的「index」指令,這是因?yàn)槎鄠€(gè)「location」是平級(jí)的關(guān)系,不存在繼承,此時(shí)應(yīng)該在「server」里定義「index」,借助繼承關(guān)系,「index」指令在所有的「location」中都能生效。

2) 使用try_files

接下來看看「if」指令,說它是大家誤解最深的Nginx指令毫不為過:

if (!-e $request_filename) {

rewrite . /index.php last;

}

很多人喜歡用「if」指令做一系列的檢查,不過這實(shí)際上是「try_files」指令的職責(zé):

try_files $uri $uri/ /index.php;

除此以外,初學(xué)者往往會(huì)認(rèn)為「if」指令是內(nèi)核級(jí)的指令,但是實(shí)際上它是rewrite模塊的一部分,加上Nginx配置實(shí)際上是聲明式的,而非過程式的,所以當(dāng)其和非rewrite模塊的指令混用時(shí),結(jié)果可能會(huì)非你所愿。

3)fastcgi_params」配置文件:

include fastcgi_params;

Nginx有兩份fastcgi配置文件,分別是「fastcgi_params」和「fastcgi.conf」,它們沒有太大的差異,唯一的區(qū)別是后者比前者多了一行「SCRIPT_FILENAME」的定義:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

注意:$document_root 和 $fastcgi_script_name 之間沒有 /。

原本Nginx只有「fastcgi_params」,后來發(fā)現(xiàn)很多人在定義「SCRIPT_FILENAME」時(shí)使用了硬編碼的方式,于是為了規(guī)范用法便引入了「fastcgi.conf」。

不過這樣的話就產(chǎn)生一個(gè)疑問:為什么一定要引入一個(gè)新的配置文件,而不是修改舊的配置文件?這是因?yàn)椤竑astcgi_param」指令是數(shù)組型的,和普通指令相同的是:內(nèi)層替換外層;和普通指令不同的是:當(dāng)在同級(jí)多次使用的時(shí)候,是新增而不是替換。換句話說,如果在同級(jí)定義兩次「SCRIPT_FILENAME」,那么它們都會(huì)被發(fā)送到后端,這可能會(huì)導(dǎo)致一些潛在的問題,為了避免此類情況,便引入了一個(gè)新的配置文件。

此外,我們還需要考慮一個(gè)安全問題:在PHP開啟「cgi.fix_pathinfo」的情況下,PHP可能會(huì)把錯(cuò)誤的文件類型當(dāng)作PHP文件來解析。如果Nginx和PHP安裝在同一臺(tái)服務(wù)器上的話,那么最簡單的解決方法是用「try_files」指令做一次過濾:

try_files $uri =404;

依照前面的分析,給出一份改良后的版本,是不是比開始的版本清爽了很多:

server {

listen 80;

server_name foo.com;

root /path;

index index.html index.htm index.php;

location / {

try_files $uri $uri/ /index.php;

}

location ~ .php$ {

try_files $uri =404;

include fastcgi.conf;

fastcgi_pass 127.0.0.1:9000;

}

}

五 Nginx為啥性能高-多進(jìn)程IO模型

1、nginx采用多進(jìn)程模型好處

首先,對(duì)于每個(gè)worker進(jìn)程來說,獨(dú)立的進(jìn)程,不需要加鎖,所以省掉了鎖帶來的開銷,同時(shí)在編程以及問題查找時(shí),也會(huì)方便很多。

其次,采用獨(dú)立的進(jìn)程,可以讓互相之間不會(huì)影響,一個(gè)進(jìn)程退出后,其它進(jìn)程還在工作,服務(wù)不會(huì)中斷,master進(jìn)程則很快啟動(dòng)新的worker進(jìn)程。當(dāng)然,worker進(jìn)程的異常退出,肯定是程序有bug了,異常退出,會(huì)導(dǎo)致當(dāng)前worker上的所有請(qǐng)求失敗,不過不會(huì)影響到所有請(qǐng)求,所以降低了風(fēng)險(xiǎn)。

nginx多進(jìn)程事件模型:異步非阻塞

雖然nginx采用多worker的方式來處理請(qǐng)求,每個(gè)worker里面只有一個(gè)主線程,那能夠處理的并發(fā)數(shù)很有限啊,多少個(gè)worker就能處理多少個(gè)并發(fā),何來高并發(fā)呢?非也,這就是nginx的高明之處,nginx采用了異步非阻塞的方式來處理請(qǐng)求,也就是說,nginx是可以同時(shí)處理成千上萬個(gè)請(qǐng)求的。

一個(gè)worker進(jìn)程可以同時(shí)處理的請(qǐng)求數(shù)只受限于內(nèi)存大小,而且在架構(gòu)設(shè)計(jì)上,不同的worker進(jìn)程之間處理并發(fā)請(qǐng)求時(shí)幾乎沒有同步鎖的限制,worker進(jìn)程通常不會(huì)進(jìn)入睡眠狀態(tài),因此,當(dāng)Nginx上的進(jìn)程數(shù)與CPU核心數(shù)相等時(shí)(最好每一個(gè)worker進(jìn)程都綁定特定的CPU核心),進(jìn)程間切換的代價(jià)是最小的。

而apache的常用工作方式(apache也有異步非阻塞版本,但因其與自帶某些模塊沖突,所以不常用),每個(gè)進(jìn)程在一個(gè)時(shí)刻只處理一個(gè)請(qǐng)求,因此,當(dāng)并發(fā)數(shù)上到幾千時(shí),就同時(shí)有幾千的進(jìn)程在處理請(qǐng)求了。這對(duì)操作系統(tǒng)來說,是個(gè)不小的挑戰(zhàn),進(jìn)程帶來的內(nèi)存占用非常大,進(jìn)程的上下文切換帶來的cpu開銷很大,自然性能就上不去了,而這些開銷完全是沒有意義的。

就這六點(diǎn),5分鐘徹底搞清楚nginx工作原理【程序員必備】

 

為什么nginx可以采用異步非阻塞的方式來處理呢,或者異步非阻塞到底是怎么回事呢?

我們先回到原點(diǎn),看看一個(gè)請(qǐng)求的完整過程:首先,請(qǐng)求過來,要建立連接,然后再接收數(shù)據(jù),接收數(shù)據(jù)后,再發(fā)送數(shù)據(jù)。

具體到系統(tǒng)底層,就是讀寫事件,而當(dāng)讀寫事件沒有準(zhǔn)備好時(shí),必然不可操作,如果不用非阻塞的方式來調(diào)用,那就得阻塞調(diào)用了,事件沒有準(zhǔn)備好,那就只能等了,等事件準(zhǔn)備好了,你再繼續(xù)吧。阻塞調(diào)用會(huì)進(jìn)入內(nèi)核等待,cpu就會(huì)讓出去給別人用了,對(duì)單線程的worker來說,顯然不合適,當(dāng)網(wǎng)絡(luò)事件越多時(shí),大家都在等待呢,cpu空閑下來沒人用,cpu利用率自然上不去了,更別談高并發(fā)了。好吧,你說加進(jìn)程數(shù),這跟apache的線程模型有什么區(qū)別,注意,別增加無謂的上下文切換。

所以,在nginx里面,最忌諱阻塞的系統(tǒng)調(diào)用了。不要阻塞,那就非阻塞嘍。非阻塞就是,事件沒有準(zhǔn)備好,馬上返回EAGAIN,告訴你,事件還沒準(zhǔn)備好呢,你慌什么,過會(huì)再來吧。好吧,你過一會(huì),再來檢查一下事件,直到事件準(zhǔn)備好了為止,在這期間,你就可以先去做其它事情,然后再來看看事件好了沒。雖然不阻塞了,但你得不時(shí)地過來檢查一下事件的狀態(tài),你可以做更多的事情了,但帶來的開銷也是不小的。

nginx支持的事件模型如下:

Nginx支持如下處理連接的方法(I/O復(fù)用方法),這些方法可以通過use指令指定。

select– 標(biāo)準(zhǔn)方法。 如果當(dāng)前平臺(tái)沒有更有效的方法,它是編譯時(shí)默認(rèn)的方法。你可以使用配置參數(shù) –with-select_module 和 –without-select_module 來啟用或禁用這個(gè)模塊。

poll– 標(biāo)準(zhǔn)方法。 如果當(dāng)前平臺(tái)沒有更有效的方法,它是編譯時(shí)默認(rèn)的方法。你可以使用配置參數(shù) –with-poll_module 和 –without-poll_module 來啟用或禁用這個(gè)模塊。

kqueue– 高效的方法,使用于 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 macOS X. 使用雙處理器的MacOS X系統(tǒng)使用kqueue可能會(huì)造成內(nèi)核崩潰。

epoll – 高效的方法,使用于Linux內(nèi)核2.6版本及以后的系統(tǒng)。在某些發(fā)行版本中,如SuSE 8.2, 有讓2.4版本的內(nèi)核支持epoll的補(bǔ)丁。

rtsig – 可執(zhí)行的實(shí)時(shí)信號(hào),使用于Linux內(nèi)核版本2.2.19以后的系統(tǒng)。默認(rèn)情況下整個(gè)系統(tǒng)中不能出現(xiàn)大于1024個(gè)POSIX實(shí)時(shí)(排隊(duì))信號(hào)。這種情況 對(duì)于高負(fù)載的服務(wù)器來說是低效的;所以有必要通過調(diào)節(jié)內(nèi)核參數(shù) /proc/sys/kernel/rtsig-max 來增加隊(duì)列的大小。

可是從Linux內(nèi)核版本2.6.6-mm2開始, 這個(gè)參數(shù)就不再使用了,并且對(duì)于每個(gè)進(jìn)程有一個(gè)獨(dú)立的信號(hào)隊(duì)列,這個(gè)隊(duì)列的大小可以用 RLIMIT_SIGPENDING 參數(shù)調(diào)節(jié)。當(dāng)這個(gè)隊(duì)列過于擁塞,nginx就放棄它并且開始使用 poll 方法來處理連接直到恢復(fù)正常。

/dev/poll – 高效的方法,使用于 Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+.

eventport – 高效的方法,使用于 Solaris 10. 為了防止出現(xiàn)內(nèi)核崩潰的問題, 有必要安裝這個(gè) 安全補(bǔ)丁。

在linux下面,只有epoll是高效的方法

下面再來看看epoll到底是如何高效的

Epoll是Linux內(nèi)核為處理大批量句柄而作了改進(jìn)的poll。 要使用epoll只需要這三個(gè)系統(tǒng)調(diào)用:epoll_create(2), epoll_ctl(2), epoll_wait(2)。它是在2.5.44內(nèi)核中被引進(jìn)的(epoll(4) is a new API introduced in Linux kernel 2.5.44),在2.6內(nèi)核中得到廣泛應(yīng)用。

epoll的優(yōu)點(diǎn)

支持一個(gè)進(jìn)程打開大數(shù)目的socket描述符(FD)

select 最不能忍受的是一個(gè)進(jìn)程所打開的FD是有一定限制的,由FD_SETSIZE設(shè)置,默認(rèn)值是2048。對(duì)于那些需要支持的上萬連接數(shù)目的IM服務(wù)器來說顯 然太少了。這時(shí)候你一是可以選擇修改這個(gè)宏然后重新編譯內(nèi)核,不過資料也同時(shí)指出這樣會(huì)帶來網(wǎng)絡(luò)效率的下降,二是可以選擇多進(jìn)程的解決方案(傳統(tǒng)的 Apache方案)。

不過雖然linux上面創(chuàng)建進(jìn)程的代價(jià)比較小,但仍舊是不可忽視的,加上進(jìn)程間數(shù)據(jù)同步遠(yuǎn)比不上線程間同步的高效,所以也不是一種完 美的方案。不過 epoll則沒有這個(gè)限制,它所支持的FD上限是最大可以打開文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048,舉個(gè)例子,在1GB內(nèi)存的機(jī)器上大約是10萬左 右,具體數(shù)目可以cat /proc/sys/fs/file-max察看,一般來說這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。

IO效率不隨FD數(shù)目增加而線性下降

傳統(tǒng)的select/poll另一個(gè)致命弱點(diǎn)就是當(dāng)你擁有一個(gè)很大的socket集合,不過由于網(wǎng)絡(luò)延時(shí),任一時(shí)間只有部分的socket是”活躍”的,但 是select/poll每次調(diào)用都會(huì)線性掃描全部的集合,導(dǎo)致效率呈現(xiàn)線性下降。但是epoll不存在這個(gè)問題,它只會(huì)對(duì)”活躍”的socket進(jìn)行操 作—這是因?yàn)樵趦?nèi)核實(shí)現(xiàn)中epoll是根據(jù)每個(gè)fd上面的callback函數(shù)實(shí)現(xiàn)的。

那么,只有”活躍”的socket才會(huì)主動(dòng)的去調(diào)用 callback函數(shù),其他idle狀態(tài)socket則不會(huì),在這點(diǎn)上,epoll實(shí)現(xiàn)了一個(gè)”偽”AIO,因?yàn)檫@時(shí)候推動(dòng)力在os內(nèi)核。在一些 benchmark中,如果所有的socket基本上都是活躍的—比如一個(gè)高速LAN環(huán)境,epoll并不比select/poll有什么效率,相 反,如果過多使用epoll_ctl,效率相比還有稍微的下降。

但是一旦使用idle connections模擬WAN環(huán)境,epoll的效率就遠(yuǎn)在select/poll之上了。

使用mmap加速內(nèi)核與用戶空間的消息傳遞。

這點(diǎn)實(shí)際上涉及到epoll的具體實(shí)現(xiàn)了。無論是select,poll還是epoll都需要內(nèi)核把FD消息通知給用戶空間,如何避免不必要的內(nèi)存拷貝就很 重要,在這點(diǎn)上,epoll是通過內(nèi)核于用戶空間mmap同一塊內(nèi)存實(shí)現(xiàn)的。而如果你想我一樣從2.5內(nèi)核就關(guān)注epoll的話,一定不會(huì)忘記手工 mmap這一步的。

內(nèi)核微調(diào)

這一點(diǎn)其實(shí)不算epoll的優(yōu)點(diǎn)了,而是整個(gè)linux平臺(tái)的優(yōu)點(diǎn)。也許你可以懷疑linux平臺(tái),但是你無法回避linux平臺(tái)賦予你微調(diào)內(nèi)核的能力。比如,內(nèi)核TCP/IP協(xié) 議棧使用內(nèi)存池管理sk_buff結(jié)構(gòu),那么可以在運(yùn)行時(shí)期動(dòng)態(tài)調(diào)整這個(gè)內(nèi)存pool(skb_head_pool)的大小— 通過echo XXXX>/proc/sys/net/core/hot_list_length完成。

再比如listen函數(shù)的第2個(gè)參數(shù)(TCP完成3次握手 的數(shù)據(jù)包隊(duì)列長度),也可以根據(jù)你平臺(tái)內(nèi)存大小動(dòng)態(tài)調(diào)整。更甚至在一個(gè)數(shù)據(jù)包面數(shù)目巨大但同時(shí)每個(gè)數(shù)據(jù)包本身大小卻很小的特殊系統(tǒng)上嘗試最新的NAPI網(wǎng)卡驅(qū)動(dòng)架構(gòu)。

推薦設(shè)置worker的個(gè)數(shù)為cpu的核數(shù),在這里就很容易理解了,更多的worker數(shù),只會(huì)導(dǎo)致進(jìn)程來競爭cpu資源了,從而帶來不必要的上下文切換。

而且,nginx為了更好的利用多核特性,提供了cpu親緣性的綁定選項(xiàng),我們可以將某一個(gè)進(jìn)程綁定在某一個(gè)核上,這樣就不會(huì)因?yàn)檫M(jìn)程的切換帶來cache的失效。像這種小的優(yōu)化在nginx中非常常見,同時(shí)也說明了nginx作者的苦心孤詣。比如,nginx在做4個(gè)字節(jié)的字符串比較時(shí),會(huì)將4個(gè)字符轉(zhuǎn)換成一個(gè)int型,再作比較,以減少cpu的指令數(shù)等等。

作者:規(guī)速

鏈接:https://blog.csdn.net/hguisu/article/details/8930668

(本文在原文基礎(chǔ)上有做一定修改,術(shù)語用詞優(yōu)化,突出要點(diǎn))

分享到:
標(biāo)簽:nginx
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績?cè)u(píng)定