初探nginx架構###
淘寶團隊的nginx教材
nginx版本1.12
- nginx與外界提陶,nginx的master與worker之間都是通過信號相連接的恩掷。
例子:從容的重啟
master接受到信號后栗弟,重新加載配置检疫,用新配置fork新的worker出來绒尊。
master向老的worker發(fā)送信號数焊,老的worker收到信號后乍惊,不再接受處理新的連接蔫耽。
老的worker處理完現(xiàn)有連接后撩炊,從容退出外永。
這樣,利用信號就達到了不中斷服務的重新加載配置拧咳。
相關代碼:[20170512更新]
1)ngx_process.c:signals全局數(shù)組伯顶,line 39,定義了所有的信號名(nginx自定名和系統(tǒng)信號名的對應)
2)ngx_config.h:NGX_RECONFIGURE_SIGNAL 被定義為SIGHUP
3)ngx_process.c:ngx_signal_handler骆膝,所有的信號處理函數(shù)都被歸到一個信號處理函數(shù)中祭衩,用switch處理,對于worker進程阅签,SIGHUP被忽略了掐暮,對于主進程,它把一個全局的變量(類型sig_atomic_t政钟,待擴展)ngx_reconfigure設置成了1路克。
4)ngx_process_cycle.c(os/unix):3)提到的ngx_reconfigure被設置成1之后,主循環(huán)有一個判斷锥涕,調用了ngx_start_worker_processes衷戈。重新建立了幾個類型為JUST_RESPAWN的worker,這些是新worker层坠。然后向所有進程發(fā)送NGX_SHUTDOWN_SIGNAL(定義為SIGQUIT)
5)3中ngx_signal_handler中worker部分殖妇,對NGX_SHUTDOWN_SIGNAL的處理是置全局變量ngx_quit = 1;前面提到的worker_process_cycle中對這個進行了判斷破花,關閉監(jiān)聽套接字谦趣,關閉空閑連接,然后退出進程座每。(關于SIGCHLD的跟蹤待擴展)
6)子進程退出會有SIGCHLD信號觸發(fā)前鹅,ngx_signal_handler會設置ngx_reap = 1并且waitpid等到退出子進程的pid,線性遍歷數(shù)組找到子進程的控制塊峭梳,設置.exited = 1舰绘;主進程會去reap .exited=1的子進程蹂喻。
結論:SIGHUP觸發(fā)主進程的RECONFIGURE動作==>主進程建立新worker==>向所有老worker發(fā)送SIGQUIT信號==>收到>SIGQUIT的老worker掃尾退出==>主進程去收割老worker的尸體。熱重啟完成捂寿。
- nginx保證每個worker等概率接受處理請求的方法
nginx沒有用一個線程接收然后分發(fā)到各個線程中去的做法口四。master進程初始化listenfd,然后fork出worker進程秦陋,worker進程持有l(wèi)istenfd蔓彩,在注冊讀事件前搶一個全局的accept_mutex互斥鎖,搶到互斥鎖的那個進程驳概,向epoll注冊讀事件赤嚼,所以接受連接這件事,都是worker在做顺又。
但是這樣做肯定會有不公平的事情發(fā)生:ngx_accept_disabled變量更卒,它定義為worker所能承受的最大連接數(shù) * 1/8 - 空閑連接數(shù),當空閑連接數(shù)小于最大連接數(shù)的1/8時稚照,ngx_accept_disabled大于零逞壁,此時的worker就不會去搶鎖了,轉而變?yōu)槊看螕屾i的時候锐锣,會將ngx_accept_disabled減1,直至該變量小于0绳瘟。
相關代碼:
ngx_event.c:try_lock_accept_mutex()去獲得鎖雕憔,然后獲得鎖的進程去注冊accept事件。
美團面試問到:如果你自己設計服務器糖声,設計為多進程還是多線程斤彼?
答案幾乎一定是多進程,就是因為進程CRASH了蘸泻,其他的進程還不受影響琉苇,只影響一部分服務,線程如果CRASH了
整個進程就都沒了悦施。