Nginx工作原理(Master+Worker

Nginx工作原理(Master+Worker)

1. Nginx
  首先要明白本涕,Nginx 采用的是多進程(單線程) & 多路IO復用模型遵绰。使用了 I/O 多路復用技術(shù)的 Nginx草巡,就成了”并發(fā)事件驅(qū)動“的服務器花枫。

image

2. 多進程的工作模式

  • 1黍瞧、Nginx 在啟動后佛舱,會有一個 master 進程和多個相互獨立的 worker 進程椎例。
  • 2挨决、接收來自外界的信號,向各worker進程發(fā)送信號订歪,每個進程都有可能來處理這個連接脖祈。
  • 3、 master 進程能監(jiān)控 worker 進程的運行狀態(tài)刷晋,當 worker 進程退出后(異常情況下)撒犀,會自動啟動新的 worker 進程。

3.注意:

worker 進程數(shù)掏秩,一般會設置成機器 cpu 核數(shù)或舞。因為更多的worker 數(shù),只會導致進程相互競爭 cpu蒙幻,從而帶來不必要的上下文切換映凳。

使用多進程模式,不僅能提高并發(fā)率邮破,而且進程之間相互獨立诈豌,一個 worker 進程掛了不會影響到其他 worker 進程。

4.驚群現(xiàn)象

  • 主進程(master 進程)首先通過 socket() 來創(chuàng)建一個 sock 文件描述符用來監(jiān)聽抒和,然后fork生成子進程(workers 進程)矫渔,子進程將繼承父進程的 sockfd(socket 文件描述符),之后子進程 accept() 后將創(chuàng)建已連接描述符(connected descriptor))摧莽,然后通過已連接描述符來與客戶端通信庙洼。
  • 那么,由于所有子進程都繼承了父進程的 sockfd镊辕,那么當連接進來時油够,所有子進程都將收到通知并“爭著”與它建立連接,這就叫“驚群現(xiàn)象”征懈。大量的進程被激活又掛起石咬,只有一個進程可以accept() 到這個連接,這當然會消耗系統(tǒng)資源卖哎。

5.Nginx對驚群現(xiàn)象的處理:
  Nginx 提供了一個 accept_mutex 這個東西鬼悠,這是一個加在accept上的一把互斥鎖。即每個 worker 進程在執(zhí)行 accept 之前都需要先獲取鎖亏娜,獲取不到就放棄執(zhí)行 accept()焕窝。有了這把鎖之后,同一時刻照藻,就只會有一個進程去 accpet()袜啃,這樣就不會有驚群問題了汗侵。accept_mutex 是一個可控選項幸缕,我們可以顯示地關(guān)掉群发,默認是打開的。

6.worker進程工作流程
  當一個 worker 進程在 accept() 這個連接之后发乔,就開始讀取請求熟妓,解析請求,處理請求栏尚,產(chǎn)生數(shù)據(jù)后起愈,再返回給客戶端,最后才斷開連接译仗,一個完整的請求抬虽。一個請求,完全由 worker 進程來處理纵菌,而且只能在一個 worker 進程中處理阐污。

這樣做帶來的好處:

  • 1、節(jié)省鎖帶來的開銷咱圆。每個 worker 進程都是獨立的進程笛辟,不共享資源,不需要加鎖序苏。同時在編程以及問題查上時手幢,也會方便很多。
  • 2忱详、獨立進程围来,減少風險。采用獨立的進程匈睁,可以讓互相之間不會影響管钳,一個進程退出后,其它進程還在工作软舌,服務不會中斷才漆,master 進程則很快重新啟動新的 worker 進程。當然佛点,worker 進程的也能發(fā)生意外退出醇滥。

7.多進程模型每個進程/線程只能處理一路IO,那么 Nginx是如何處理多路IO呢超营?

  1. 如果不使用 IO 多路復用鸳玩,那么在一個進程中,同時只能處理一個請求演闭,比如執(zhí)行 accept()不跟,如果沒有連接過來,那么程序會阻塞在這里米碰,直到有一個連接過來窝革,才能繼續(xù)向下執(zhí)行购城。
  2. 而多路復用,允許我們只在事件發(fā)生時才將控制返回給程序虐译,而其他時候內(nèi)核都掛起進程瘪板,隨時待命。

核心:Nginx采用的 IO多路復用模型epoll
  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中的事件都會與設備驅(qū)動程序建立回調(diào)關(guān)系回季,也就是說家制,當相應的事件發(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 了。

epoll() 中內(nèi)核則維護一個鏈表塑娇,epoll_wait 直接檢查鏈表是不是空就知道是否有文件描述符準備好了澈侠。(epoll 與 select 相比最大的優(yōu)點是不會隨著 sockfd 數(shù)目增長而降低效率,使用 select() 時埋酬,內(nèi)核采用輪訓的方法來查看是否有fd 準備好哨啃,其中的保存 sockfd 的是類似數(shù)組的數(shù)據(jù)結(jié)構(gòu) fd_set,key 為 fd写妥,value 為 0 或者 1拳球。)

能達到這種效果,是因為在內(nèi)核實現(xiàn)中 epoll 是根據(jù)每個 sockfd 上面的與設備驅(qū)動程序建立起來的回調(diào)函數(shù)實現(xiàn)的珍特。那么祝峻,某個 sockfd 上的事件發(fā)生時,與它對應的回調(diào)函數(shù)就會被調(diào)用,來把這個 sockfd 加入鏈表莱找,其他處于“空閑的”狀態(tài)的則不會酬姆。在這點上,epoll 實現(xiàn)了一個”偽”AIO宋距。但是如果絕大部分的 I/O 都是“活躍的”,每個 socket 使用率很高的話症脂,epoll效率不一定比 select 高(可能是要維護隊列復雜)谚赎。

可以看出,因為一個進程里只有一個線程诱篷,所以一個進程同時只能做一件事壶唤,但是可以通過不斷地切換來“同時”處理多個請求。

8.例子:

Nginx 會注冊一個事件:“如果來自一個新客戶端的連接請求到來了棕所,再通知我”闸盔,此后只有連接請求到來,服務器才會執(zhí)行 accept() 來接收請求琳省。又比如向上游服務器(比如 PHP-FPM)轉(zhuǎn)發(fā)請求迎吵,并等待請求返回時,這個處理的 worker 不會在這阻塞针贬,它會在發(fā)送完請求后击费,注冊一個事件:“如果緩沖區(qū)接收到數(shù)據(jù)了,告訴我一聲桦他,我再將它讀進來”蔫巩,于是進程就空閑下來等待事件發(fā)生。

這樣快压,基于 多進程+epoll圆仔, Nginx 便能實現(xiàn)高并發(fā)。

使用 epoll 處理事件的一個框架蔫劣,代碼轉(zhuǎn)自:http://www.cnblogs.com/fnlingnzb-learner/p/5835573.html

[
復制代碼

](javascript:void(0); "復制代碼")

<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">for( ; ; ) // 無限循環(huán)
{
nfds = epoll_wait(epfd,events,20,500); // 最長阻塞 500s
for(i=0;i<nfds;++i)
{ if(events[i].data.fd==listenfd) //有新的連接
{
connfd = accept(listenfd,(sockaddr )&clientaddr, &clilen); //accept這個連接
ev.data.fd=connfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //將新的fd添加到epoll的監(jiān)聽隊列中
} else if( events[i].events&EPOLLIN ) //接收到數(shù)據(jù)坪郭,讀socket
{
n = read(sockfd, line, MAXLINE)) < 0 //讀
ev.data.ptr = md; //md為自定義類型,添加數(shù)據(jù)
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改標識符脉幢,等待下一個循環(huán)時發(fā)送數(shù)據(jù)截粗,異步處理的精髓
} else if(events[i].events&EPOLLOUT) //有數(shù)據(jù)待發(fā)送,寫socket
{ struct myepoll_data
md = (myepoll_data)events[i].data.ptr; //取數(shù)據(jù)
sockfd = md->fd;
send( sockfd, md->ptr, strlen((char
)md->ptr), 0 ); //發(fā)送數(shù)據(jù)
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改標識符鸵隧,等待下一個循環(huán)時接收數(shù)據(jù)
} else { //其他的處理
}
}
}</pre>

[
復制代碼

](javascript:void(0); "復制代碼")

9.Nginx 與 多進程模式 Apache 的比較:
事件驅(qū)動適合于I/O密集型服務绸罗,多進程或線程適合于CPU密集型服務:

  • 1、Nginx 更主要是作為反向代理豆瘫,而非Web服務器使用珊蟀。其模式是事件驅(qū)動。
  • 2、事件驅(qū)動服務器育灸,最適合做的就是這種 I/O 密集型工作腻窒,如反向代理,它在客戶端與WEB服務器之間起一個數(shù)據(jù)中轉(zhuǎn)作用磅崭,純粹是 I/O 操作儿子,自身并不涉及到復雜計算。因為進程在一個地方進行計算時砸喻,那么這個進程就不能處理其他事件了柔逼。
  • 3、Nginx 只需要少量進程配合事件驅(qū)動割岛,幾個進程跑 libevent愉适,不像 Apache 多進程模型那樣動輒數(shù)百的進程數(shù)。
  • 4癣漆、Nginx 處理靜態(tài)文件效果也很好维咸,那是因為讀寫文件和網(wǎng)絡通信其實都是 I/O操作,處理過程一樣惠爽。

原文鏈接:https://blog.csdn.net/kim_weir/article/details/80036462

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末癌蓖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子婚肆,更是在濱河造成了極大的恐慌费坊,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旬痹,死亡現(xiàn)場離奇詭異附井,居然都是意外死亡,警方通過查閱死者的電腦和手機两残,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門永毅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人人弓,你說我怎么就攤上這事沼死。” “怎么了崔赌?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵意蛀,是天一觀的道長。 經(jīng)常有香客問我健芭,道長县钥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任慈迈,我火速辦了婚禮若贮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己谴麦,他們只是感情好蠢沿,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匾效,像睡著了一般舷蟀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上面哼,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天野宜,我揣著相機與錄音,去河邊找鬼精绎。 笑死速缨,一個胖子當著我的面吹牛锌妻,可吹牛的內(nèi)容都是我干的代乃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼仿粹,長吁一口氣:“原來是場噩夢啊……” “哼搁吓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吭历,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤堕仔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晌区,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摩骨,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年朗若,在試婚紗的時候發(fā)現(xiàn)自己被綠了恼五。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡哭懈,死狀恐怖灾馒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遣总,我是刑警寧澤睬罗,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站旭斥,受9級特大地震影響容达,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垂券,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一董饰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦卒暂、人聲如沸啄栓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昙楚。三九已至,卻和暖如春诈嘿,著一層夾襖步出監(jiān)牢的瞬間堪旧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工奖亚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淳梦,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓昔字,卻偏偏與公主長得像爆袍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子作郭,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359