什么是nginx
- 2012年成長為世界第二大web服務(wù)器
- 業(yè)內(nèi)高性能web服務(wù)器代名詞
- 競爭對手
1 Apache
2 Lighttped(受歐美界青睞的,與nginx有的一拼的)
3 Tomcat("java語言web服務(wù)器 先天就是重量級性能跟nginx沒法比")
4 Jetty("java語言web服務(wù)器 先天就是重量級性能跟nginx沒法比")
5 IIS(window系統(tǒng))
- 基于REST架構(gòu)風(fēng)格盯串,以統(tǒng)一資源定位符(URI)貨統(tǒng)一資源描述符(URL)作為溝通依據(jù)
- 基于事件驅(qū)動
- 高度模塊化的設(shè)計----->第三方模塊眾多
- 可運行在眾多平臺
可以使用當(dāng)前操作系統(tǒng)的高效API來提高自己的性能
支持linux上的epoll氯檐,epoll是大并網(wǎng)絡(luò)連接的利器
為什么是nginx
1.因為很吊,體現(xiàn)在如下幾個方面
非要用一句話總結(jié)体捏,那就是能夠支持高并發(fā)請求的同時保持高效的服務(wù)
-
更快
單次請求得到更快的響應(yīng) -
高擴展性
nginx設(shè)計極具擴展性冠摄,完全是由多個不同層次/不同功能/不同類型且耦合度極低的模塊組成
并且Nginx的模塊都是嵌入到二進制文件中執(zhí)行的
-- --比如HTTP模塊中,還設(shè)計了HTTP過濾模塊几缭,一個正常的HTTP模塊處理完請求后河泳,會有一連串的HTTP過濾模塊再對其進行過濾。
-- --我們開發(fā)一個新的HTTP模塊時奏司,可以使用HTTP核心模塊 events模塊 log模塊等 還可以自由的復(fù)用各種過濾器模塊
高可靠性
低內(nèi)存消耗
一般情況下乔询,10000個非活躍的HTTP Keep-Alive連接在Nginx中僅消耗2.5M內(nèi)存
單機支持10萬以上的并發(fā)連接
理論上,Nginx的并發(fā)連接數(shù)僅取決于內(nèi)存韵洋,10萬遠未封頂竿刁,當(dāng)然與業(yè)務(wù)特點也緊密相連
熱部署
由于master管理進程與worker工作進程的分離設(shè)計
使得nginx可以不停止服務(wù)就能升級可執(zhí)行文件/更新配置項目/更換日志文件等
nginx的架構(gòu)設(shè)計很高明
先天的事件驅(qū)動型設(shè)計
全異步的網(wǎng)絡(luò)I/O處理機制
極少的進程間切換
-
強大的開源社區(qū)
數(shù)以萬計的碼畜們?yōu)閚ginx添磚加瓦
2.為什么這么吊
這里我們著重講解一下nginx使用的事件驅(qū)動架構(gòu),簡單來說如下所示
graph LR
A[事件發(fā)生源]--產(chǎn)生事件-->B[事件收集器收集]
B--分發(fā)事件-->C[時件處理器注冊自己感興趣的事件并消費之]
事件源: 一般由網(wǎng)卡和磁盤產(chǎn)生
事件收集器: nginx的事件模塊搪缨,如ngx_epoll_module
消費者: 所有其它模塊
消費者首先向事件模塊注冊自己感興趣的事件類型食拜,當(dāng)該類型事件產(chǎn)生時,事件模塊就會把事件分發(fā)到相應(yīng)消費者模塊
nginx采用完全的事件驅(qū)動架構(gòu)來處理業(yè)務(wù)副编,那它與傳統(tǒng)的web服務(wù)器有哪些不同呢负甸?
- 傳統(tǒng)web服務(wù)器(比如Apache)
- Apache采用的所謂事件驅(qū)動僅僅是體現(xiàn)在TCP連接的建立和關(guān)閉事件上
- 一個連接建立以后到關(guān)閉之前,所有的操作不再是事件操作痹届,退化成了按序執(zhí)行每個操作
- 整個請求在連接期間始終占用cpu 內(nèi)存資源呻待,及時沒有作任何有意義的事
-
把一個進程或線程作為事件消費者
,當(dāng)一個請求產(chǎn)生事件被該進程處理時队腐,直到請求處理結(jié)束時進程資源都將被這一個請求占用
- Apache采用的所謂事件驅(qū)動僅僅是體現(xiàn)在TCP連接的建立和關(guān)閉事件上
- nginx服務(wù)器
-
不會使用進程或者線程作為事件消費者
蚕捉,所謂的事件消費者只能是某個模塊(在這里沒有進程的概念) - 只有事件收集和分發(fā)器才有資格占用進程資源
-
- 重要差別
- 前者是每個事件消費者獨占一個進程資源,后者的事件消費者只是被事件分發(fā)者進程短期調(diào)用而已
- nginx這種設(shè)計的一個弊端
- 即每個事件消費者都不能有阻塞行為柴淘,否則會長時間占用事件分發(fā)者進程而導(dǎo)致其它事件得不到及時響應(yīng)迫淹,
尤其是每個消費者不可以讓進程轉(zhuǎn)為休眠或等待狀態(tài)
,這都增加了碼畜們的開發(fā)難度
- 即每個事件消費者都不能有阻塞行為柴淘,否則會長時間占用事件分發(fā)者進程而導(dǎo)致其它事件得不到及時響應(yīng)迫淹,
手摸手教你使用nginx
準備工作
安裝nginx開發(fā)所需的最基本的庫
以下是完成web服務(wù)器功能所需要的基本包
- 使用uname -a 查看linuxe內(nèi)核是否時2.6及以上版本
因為只有l(wèi)inux2.6及以上版本才支持epoll为严,能夠更大限度發(fā)揮nginx的威力
- GCC編譯器 使用yum install -y gcc安裝
GCC(GNU Compile Collection)可以用來編譯C語言程序敛熬,因為有時候nginx不會直接提供二進制可執(zhí)行程序,需要自己編譯
- PCRE庫
Perl正則兼容表達式包第股,pcre-devel時PCRE做二次開發(fā)時所需要的開發(fā)庫应民,也是nginx開發(fā)所必需的
- zlib庫 yum install -y gzip gzip-devel
nginx.conf中配置了gzip on對Http 包的內(nèi)容作gzip格式壓縮,需要用
zlip-devel是二次開發(fā)所需要的庫 - OpenSSL庫 yum install -y openssl openssl-devel
如果想使用更安全的SSL 協(xié)議傳輸HTTP夕吻,就需要該包诲锹,如果要使用MD5或者SHA1包,也需要Openssl包
需要了解的幾個目錄
- nginx源碼存放目錄梭冠,隨便放辕狰,沒人管你,看個人癖好
- Nginx編譯階段產(chǎn)生的中間目錄
該目錄用于存放configure和make命令執(zhí)行后控漠,生成的中間目錄蔓倍,默認情況下,生產(chǎn)objs目錄存放在源碼目錄中
- 部署目錄默認
/usr/local/nginx
存放nginx運行期間所需的二進制文件盐捷,配置文件等偶翅。
- 日志文件存放目錄
如果你要研究nginx的底層架構(gòu),那么打開debug級別日志后碉渡,會產(chǎn)生大量日志聚谁,所以最好弄一個大點的磁盤
源碼編譯安裝
- 官方下載nginx源碼并解壓后,cd到源碼目錄 ./configure && make && make install
- 下面我們來看看這幾個命令做了哪些見不得人的勾當(dāng)
1 大部分工作其實都是configure命令做了滞诺,使用config --help來查看都是有哪些命令 我們一般只關(guān)心以下幾個
2 --prefix=PATH 安裝目錄形导,默認是/usr/local/nginx
--sbin-path=PATH可執(zhí)行文件防止路徑,默認是?<prefix>/sbin/nginx
--conf-path=PATH配置文件路徑环疼,默認是<prefix>/conf/nginx.conf
--error-log-path=PATH錯誤日志文件,默認<prefix>/logs/error.log
后面會在nginx.conf中詳細介紹把不同請求的錯誤日志打到不同的log文件中
--pid-path=PATH pid文件存放目錄,默認<prefix>/logs/nginx.pid 這個文件以ascii碼存放著nginx master的進程id
更多的請自行搜索~~~~
必須知道的命令
/usr/local/nginx/sbin/nginx命令
默認加載/usr/local/nginx/conf/nginx.conf
/usr/local/nginx/sbin/nginx -c <配置文件目錄> 來啟動非默認的配置
/usr/local/nginx/sbin/nginx -p <目錄> 來指定nginx的安裝目錄
/usr/local/nginx/sbin/nginx -g 來臨時指定一些全局配置項
/usr/local/nginx/sbin/nginx -g "pid /var/nginx/test.pid;"
意味著把pid寫到另一個文件中朵耕,-g指定的不能與默認沖突炫隶,另外以-g啟動的ngix在停止事也需要加上-g
/usr/local/nginx/sbin/nginx -g "pid /var/nginx/test.pid;" -s stop. 如果不加-g 就找不到pid文件了
/usr/local/nginx/sbin/nginx -t
不啟動nginx情況下,測試配置文件將是否有誤
/usr/local/nginx/sbin/nginx -V
顯示版本信息
/usr/local/nginx/sbin/nginx -s stop
快速停止服務(wù)處理完當(dāng)前正在處理的請求后阎曹,關(guān)閉服務(wù)
/usr/local/nginx/sbin/nginx -s reload
運行中的nginx重新加載nginix.conf 等效于kill -s SIGHUP <nginx master pid>
/usr/local/nginx/sbin/nginx -s reopen
等效于kill -s SIGUSR1 <nginx master pid>
可以重新打開配置文件伪阶,這樣我們就可以把當(dāng)前的日志文件改名或者移動,使其不會太大
平滑升級nginx
1.kill -s SIGUSR2 <nginx master pid> 會將nginx.pid重命名為nginx.pid.oldbin
2.使用命令啟動nginx
3.使用kill -s SIGQUIT <舊版本的master pid> 關(guān)閉舊版本服務(wù)
nginx配置
1 生產(chǎn)環(huán)境一般都是一個master進程管理多個worker進程处嫌,worker進程與cpu核心數(shù)相等栅贴,每個worker都能繁忙的提供服務(wù)處理。
2 master進程只負責(zé)worker的管理熏迹。worker進程之間通過共享內(nèi)存檐薯,原子操作等進程間通信機制實現(xiàn)負載均衡等功能
3 nginx是支持單進程 (只有一個master)提供服務(wù)的。使用master+worker的優(yōu)勢如下
1master只提供純管理工作癣缅,只提供命令行服務(wù)厨剪。
2多worker進程可以提高服務(wù)的健壯型,可以充分利用多核cpu
為什么需要把nginx的worker 進程數(shù)量設(shè)置的根cpu核心數(shù)一致呢
- 在apache上每個進程每一時刻只能處理一個請求友存,因此要想并發(fā)處理更多的并發(fā)請求數(shù)就要設(shè)置很多個進程祷膳,而大量的進程間切換很消耗內(nèi)存資源
- 而nginx的一個worker進程處理的請求數(shù)只受限于內(nèi)存大小,并且worker進程之間處理并發(fā)請求時幾乎沒有同步鎖的限制屡立,worker進程也不會進入睡眠狀態(tài)直晨,因此當(dāng)nginx進程與cpu核數(shù)一致時(最好每一個worker綁定一個內(nèi)核),進程切換的代價是最小的
- 將進程與cpu綁定膨俐,這樣不會出現(xiàn)多個進程搶占一個cpu的問題勇皇,就不會出現(xiàn)同步問題,這樣在內(nèi)核的調(diào)度上就實現(xiàn)了完全的并發(fā)
nginx.conf文件說明
nginx的配置文件采用塊配置項的組織方式焚刺,如下所示
全局配置
塊配置項名1 {
配置項名 配置項值1 配置項值1;
}
塊配置項名2 參數(shù) {
配置項名 配置項值1 配置項值1;
}
基本的塊配置項有:events http server location upstreams 快配置項可以嵌套
配置項名必須是nginx的某一個配置模塊中想要處理的敛摘,否則會出錯
如果配置項值中包括語法符號,比如空格乳愉,需要用引號括注配置項值
配置項的單位兄淫,如果是空間大小是 k或者m
有些模塊允許配置項值中使用變量,變量前加上$
一個具體的nginx.conf配置說明如下
可能包括以下幾部分蔓姚,這里我盡可能多的寫幾個哈~方便自己以后回頭查閱
-
全局配置
- user username [groupname];
- 用于當(dāng)master進程啟動后,fork出的worker進程運行在哪個用戶和用戶組下
- 需要在configure時使用參數(shù) --user=username --group=groupname
- daemo on|off; 是否以守護進程方式運行服務(wù)捕虽,默認on
- master_process on|off; 是否以master/worker方式工作 默認on
- error_log /path/file level; 錯誤日志 默認logs/error.log error
- 可以是/dev/null,這樣就不會有任何日志了,這也是關(guān)閉error日志的唯一手段了
- 如果日志級別寫成debug坡脐,那么在最初configure時需要加上--with-debug配置項
- woker_rlimit_nofile limit; 一個worker進程可以打開的最大句柄描述符個數(shù)
- worker_rlimit_core size;
- worker_directory path;
- 當(dāng)進程意外終止時泄私,nginx會把進程執(zhí)行時的內(nèi)存內(nèi)容轉(zhuǎn)儲到一個core文件中,方便我們查看寄存器堆棧來定位問題,上面兩個配置設(shè)定這個文件的大小和目錄
- env VAR|VAR=VALUE 這個配置項可以讓用戶直接操作系統(tǒng)變量
- include /path/file
- 將其它配置文件嵌入到nginx.conf中晌端,它的目錄可以是絕對的捅暴,也可以是相對的,相對nginx.conf所在目錄
- worker_process 4斩松;
- worder_cpu_affinity 1000 0100 0010 0001
- 上面兩個配置將worder進程與cpu實現(xiàn)綁定
- worker_priority nice nginx 的進程優(yōu)先級配置s
- user username [groupname];
-
事件類配置
events {
debug_connection IP; 只對該ip的請求才輸出debug級別的日志伶唯,可以通過該方法定位bug accept_mutex [on|off]; 負載均衡鎖觉既,默認打開惧盹,如果關(guān)閉建立TCP鏈接的耗時更短,但每個worker的負載會非常不均衡 lock_file path/file; accept鎖需要這個文件瞪讼,如果由于程序的編譯和操作系統(tǒng)的架構(gòu)等因素導(dǎo)致nginx不支持原子鎖, 就會用文件鎖來實現(xiàn)accept鎖钧椰。如果支持原子鎖,這個文件就沒意義了 accept_mutex_delay Nms; 使用文件鎖后符欠,同一時間只有一個worker能獲得到這個鎖,這個鎖不是阻塞所,worker獲取不到嫡霞,會立即返回,然后間隔這個時間之后再去獲取。 multi_accept on|off;默認關(guān)閉希柿,當(dāng)事件模型通知有新連接時诊沪,盡可能的對本次調(diào)度中客戶端發(fā)起的所有的TCP請求都去建立連接 use [poll| select | epoll| kqueue]; Nginx 默認會選擇最合適的事件模型 woker_connections number; 每個worker進程可以同時處理的最大連接數(shù)
}
-
http模塊
靜態(tài)web服務(wù)器主要由nginx中的ngx_http_core_module實現(xiàn)
http模塊是一個最小靜態(tài)web服務(wù)器的基本配置http {
gzip on; server { listen address:port; address可以是ip或者hostname 在port后可以加上一些參數(shù) 如下所示 listen 443 default_server ssl deferred; deault_server當(dāng)一個請求無法匹配所有的域名后,使用這個作為默認處理域名 ssl 在當(dāng)前端口的連接必須是基于SSL協(xié)議 deferred用戶發(fā)起建立連接請求曾撤,完成TCP三次握手之后端姚,內(nèi)核也不會調(diào)度worker進程來處理這次鏈接, 只有用戶真的發(fā)了數(shù)據(jù)(網(wǎng)卡收到請求數(shù)據(jù)包)內(nèi)核才會喚醒worker進程去處理, server_name name ;后面可以跟多個主機名稱,用頓號隔開 'nginx 收到請求后挤悉,會先取出Header頭中的host渐裸,根server中去比較,如果匹配了多個server,會根據(jù)匹配優(yōu)先級來選擇用哪個server, 如果都找不到装悲,就用server_name 為空的server塊' server_name_in_redirect on|off 默認為on '如果為開啟昏鹃,那么首先查找server_name,如果沒有找到诀诊,查找請求頭的HOST字段洞渤,如果沒有,則以當(dāng)前服務(wù)器的IP進行拼接 location [=|~|~*|^~|@] /uri/ {} uri參數(shù)里可以用正則 location = / {} 用戶請求是/時匹配 ~ URI大小寫敏感 ~* 匹配URI時忽略大小寫 ^~前半部分大小寫敏感匹配属瓣,如 location ^~ /images/ {}以/images/ 開始的請求都會匹配 @表示nginx內(nèi)部請求之間的重定向,不直接處理用戶請求 root path 文件路徑载迄,默認是root html root配置還可以位于http模塊下或者location下,如果位于location下含義如下 location /download/ { root /opt/web/html/; } 如果用戶的請求是/download/test.html,web服務(wù)器將會返回服務(wù)器上的/opt/web/html/download/test.html文件中內(nèi)容 location /conf { location下配置說明 alias /usr/local/nginx/conf/ 如果用戶請求是/conf/nginx.conf,用戶實際想訪問/usr/local/nginx/conf/nginx.conf,就可以使用alias配置 alias只能放在location中 root path; index 首頁文件; } error_page code uri|@named_location 也可以在location塊中配置 可以作如下配置 error_code 404 /404.html error_code 501 502 504 /50x.html error_code 403 http://example.com/forbidden.html error_code 404 = @fetch 也可以改變錯誤碼 error_page 404 =200 /empty.gif 或者不指定錯誤碼 奠涌,由重定向后的實際處理決定 error_page 404 /empty.gif 如果不想修改uri,只是想定向到另一個location中處理宪巨,可以如下配置 location / { error_code 404 @fallback } location @fallback { proxy_pass http://backend; } try_files path1 path2 uri 嘗試訪問每一個path找到了就結(jié)束請求 ,都找不到久落到了uri上,所以uri必須存在 type { MIME類型設(shè)置,可以位于server location塊中 type說白了就是不同的文件類型用不同的應(yīng)用程序打開溜畅。 } } "http中還有賊多的配置 比如tcp網(wǎng)絡(luò)鏈接 內(nèi)存資源管理 對客戶端請求的限制 文件操作的優(yōu)化等等捏卓,等用到了可以具體研究"
}
反向代理服務(wù)器配置
nginx的高并發(fā)高負載能力相當(dāng)彪悍,一般可以直接作為web服務(wù)器向用戶提供靜態(tài)文件服務(wù)。但有些復(fù)雜業(yè)務(wù)不適合直接放在nginx服務(wù)器上怠晴,這時候會用Apache等服務(wù)器來處理,使用nginx作為靜態(tài)web服務(wù)器和反反向代理服務(wù)器遥金,不適合nginx處理的請求直接轉(zhuǎn)到上游服務(wù)器處理
nginx反向代理方式
HTTP請求--->nginx將請求內(nèi)容落地到所在服務(wù)器硬盤或內(nèi)存---->向上游服務(wù)器發(fā)起連接
- 優(yōu)點:降低上游業(yè)務(wù)服務(wù)器的負載,盡量把壓力放在nginx服務(wù)器上
- 因為客戶端與代理服務(wù)器之間走公網(wǎng),網(wǎng)絡(luò)環(huán)境復(fù)雜蒜田,而代理服務(wù)器與上游業(yè)務(wù)服務(wù)器走的是內(nèi)部專網(wǎng)稿械,在接收完用戶請求后,在內(nèi)網(wǎng)轉(zhuǎn)發(fā)會相當(dāng)快冲粤。如果是一邊接收一邊轉(zhuǎn)發(fā)美莫,外網(wǎng)的爛速度就會拖垮內(nèi)網(wǎng)。
- 缺點: 延長了請求處理時間梯捕,增加了nginx服務(wù)器的內(nèi)存和磁盤空間
負載均衡配置
在http模塊中
http {
upstream backen {
ip_hash;
server backend1.example.com weight=5;
server backend2.example.com max_failes3 fail_timeout=30s;
}
#定義了一個叫作backend的上游服務(wù)器集群
#server 后可以跟域名 IP地址端口等
#weight轉(zhuǎn)發(fā)權(quán)重
#max_failem默認為1,0表示不檢查失敗次數(shù);faile_timeout默認為10s 30s內(nèi)轉(zhuǎn)發(fā)失敗3次厢呵,認為該服務(wù)器不可用
#ip_hash保證同一個ip的請求落到同一個服務(wù)器上,不能與weight同時使用,
并且如果服務(wù)器集群中有一臺機器掛掉傀顾,不能直接刪除該配置, 而要使用down進行標示襟铭,以保證轉(zhuǎn)發(fā)策略的一致性
#反向代理還提供了一些變量如:$remote_addr $time_local $request 等等 可以在access_log的log_format日志格式配置中使用?短曾?寒砖?
}
反向代理配置
在location模塊中,配置如下
upstream backen {
.....
}
server{
location /{
proxy_pass http://backend;
proxy_set_header Host $host
#默認情況反向代理是不會轉(zhuǎn)發(fā)請求中的host頭部,如果需要轉(zhuǎn)發(fā),使用proxy_set_header配置
}
}
nginx進階
- Nginx 采用的是多進程(單線程) & 多路IO復(fù)用模型嫉拐。并發(fā)事件驅(qū)動
- 流程說明
- 主進程(master 進程)首先通過 socket() 來創(chuàng)建一個 sock 文件描述符用來監(jiān)聽哩都,然后fork生成子進程
- 子進程將繼承父進程的 sockfd
- 之后子進程 accept() 后將創(chuàng)建已連接描述,然后通過已連接描述符來與客戶端通信
graph TB
A[管理員]--信號-->B[master進程]
B--信號-->C[worker進程]
B--信號-->D[worker進程]
B--信號-->E[worker進程]
由于所有子進程都繼承了父進程的 sockfd椭岩,那么當(dāng)連接進來時茅逮,所有子進程都將收到通知并“爭著”與它建立連接,這就叫“驚群現(xiàn)象”判哥。大量的進程被激活又掛起献雅,只有一個進程可以accept() 到這個連接,這當(dāng)然會消耗系統(tǒng)資源
Nginx對驚群現(xiàn)象的處理
Nginx 提供了一個 accept_mutex 這個東西塌计,這是一個加在accept上的一把共享鎖挺身。即每個 worker 進程在執(zhí)行 accept 之前都需要先獲取鎖,獲取不到就放棄執(zhí)行 accept()锌仅。有了這把鎖之后章钾,同一時刻,就只會有一個進程去 accpet()热芹,這樣就不會有驚群問題了贱傀。accept_mutex 是一個可控選項,我們可以顯示地關(guān)掉伊脓,默認是打開的
多進程模型每個進程/線程只能處理一路IO府寒,那么 Nginx是如何處理多路IO呢?
1 如果不使用 IO 多路復(fù)用,那么在一個進程中株搔,同時只能處理一個請求剖淀,比如執(zhí)行 accept(),如果沒有連接過來纤房,那么程序會阻塞在這里纵隔,直到有一個連接過來,才能繼續(xù)向下執(zhí)行
2 Nginx采用的 IO多路復(fù)用模型epoll
3 epoll通過在Linux內(nèi)核中申請一個簡易的文件系統(tǒng)(文件系統(tǒng)一般用什么數(shù)據(jù)結(jié)構(gòu)實現(xiàn)炮姨?B+樹)捌刮,其工作流程分為三部分
1、調(diào)用 int epoll_create(int size)建立一個epoll對象剑令,內(nèi)核會創(chuàng)建一個eventpoll結(jié)構(gòu)體糊啡,用于存放通過epoll_ctl()向epoll對象中添加進來
的事件,這些事件都會掛載在紅黑樹中吁津。
2、調(diào)用 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 在 epoll 對象中為 fd 注冊事件堕扶,所有添加到epoll中的件
都會與設(shè)備驅(qū)動程序建立回調(diào)關(guān)系碍脏,也就是說,當(dāng)相應(yīng)的事件發(fā)生時會調(diào)用這個sockfd的回調(diào)方法稍算,將sockfd添加到eventpoll 中的雙鏈表
3典尾、調(diào)用 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) 來等待事件的發(fā)生,timeout 為 -1 時糊探,該
調(diào)用會阻塞知道有事件發(fā)生
這樣钾埂,注冊好事件之后,只要有 fd 上事件發(fā)生科平,epoll_wait() 就能檢測到并返回給用戶褥紫,用戶就能”非阻塞“地進行 I/O 了。
4 epoll() 中內(nèi)核則維護一個鏈表瞪慧,epoll_wait 直接檢查鏈表是不是空就知道是否有文件描述符準備好了髓考。(epoll 與 select 相比最大的優(yōu)點是不會隨著 sockfd 數(shù)目增長而降低效率,使用 select() 時弃酌,內(nèi)核采用輪訓(xùn)的方法來查看是否有fd 準備好氨菇,其中的保存 sockfd 的是類似數(shù)組的數(shù)據(jù)結(jié)構(gòu) fd_set,key 為 fd妓湘,value 為 0 或者 1查蓉。)
5 能達到這種效果,是因為在內(nèi)核實現(xiàn)中 epoll 是根據(jù)每個 sockfd 上面的與設(shè)備驅(qū)動程序建立起來的回調(diào)函數(shù)實現(xiàn)的榜贴。那么豌研,某個 sockfd 上的事件發(fā)生時,與它對應(yīng)的回調(diào)函數(shù)就會被調(diào)用,來把這個 sockfd 加入鏈表聂沙,其他處于“空閑的”狀態(tài)的則不會秆麸。在這點上,epoll 實現(xiàn)了一個”偽”AIO及汉。但是如果絕大部分的 I/O 都是“活躍的”沮趣,每個 socket 使用率很高的話,epoll效率不一定比 select 高(可能是要維護隊列復(fù)雜)坷随。
6 例子:Nginx 會注冊一個事件:“如果來自一個新客戶端的連接請求到來了房铭,再通知我”,此后只有連接請求到來温眉,服務(wù)器才會執(zhí)行 accept() 來接收請求缸匪。又比如向上游服務(wù)器(比如 PHP-FPM)轉(zhuǎn)發(fā)請求,并等待請求返回時类溢,這個處理的 worker 不會在這阻塞凌蔬,它會在發(fā)送完請求后,注冊一個事件:“如果緩沖區(qū)接收到數(shù)據(jù)了闯冷,告訴我一聲砂心,我再將它讀進來”,于是進程就空閑下來等待事件發(fā)生蛇耀。
這樣辩诞,基于 多進程+epoll, Nginx 便能實現(xiàn)高并發(fā)纺涤。
Nginx 與 多進程模式 Apache 的比較
1 對于Apache译暂,每個請求都會獨占一個工作線程,當(dāng)并發(fā)數(shù)到達幾千時撩炊,就同時有幾千的線程在處理請求了外永。這對于操作系統(tǒng)來說,占用的內(nèi)存非常大衰抑,線程的上下文切換帶來的cpu開銷也很大象迎,性能就難以上去,同時這些開銷是完全沒有意義的
對于Nginx來講呛踊,一個進程只有一個主線程砾淌,通過異步非阻塞的事件處理機制,實現(xiàn)了循環(huán)處理多個準備好的事件谭网,從而實現(xiàn)輕量級和高并發(fā)
事件驅(qū)動適合于I/O密集型服務(wù)汪厨,多進程或線程適合于CPU密集型服務(wù)
1 Nginx 更主要是作為反向代理,而非Web服務(wù)器使用愉择。其模式是事件驅(qū)動
2 事件驅(qū)動服務(wù)器劫乱,最適合做的就是這種 I/O 密集型工作织中,如反向代理,它在客戶端與WEB服務(wù)器之間起一個數(shù)據(jù)中轉(zhuǎn)作用衷戈,純粹是 I/O 操作狭吼,自身并不涉及到復(fù)雜計算。因為進程在一個地方進行計算時殖妇,那么這個進程就不能處理其他事件了
3 Nginx 只需要少量進程配合事件驅(qū)動刁笙,幾個進程跑 libevent,不像 Apache 多進程模型那樣動輒數(shù)百的進程數(shù)
4 Nginx 處理靜態(tài)文件效果也很好谦趣,那是因為讀寫文件和網(wǎng)絡(luò)通信其實都是 I/O操作疲吸,處理過程一樣