1. 程式人生 > >Nginx 啟動原理與模型

Nginx 啟動原理與模型

操作 sele -s 這樣的 影響 非阻塞 select images load

  • Nginx 在啟動後,在 unix 系統中會以 daemon 的方式在後臺運行,後臺進程包含一個 master 進程和多個 worker 進程。
    技術分享圖片
  • master 進程主要用來管理 worker 進程,包含:接收來自外界的信號,向各 worker 進程發送信號,監控 worker 進程的運行狀態,當 worker 進程退出後(異常情況下),會自動重新啟動新的 worker 進程。
  • 多個 worker 進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。worker 進程的個數是可以設置的,一般我們會設置與機器cpu核數一致。
  • kill -HUP pid,則是告訴 Nginx,從容地重啟 Nginx,此服務是不中斷。master 進程在接收到 HUP 信號後是怎麽做的呢?首先 master 進程在接到信號後,會先重新加載配置文件,然後再啟動新的 worker 進程,並向所有老的 worker 進程發送信號,告訴他們可以光榮退休了。新的 worker 在啟動後,就開始接收新的請求,而老的 worker 在收到來自 master 的信號後,就不再接收新的請求,並且在當前進程中的所有未處理完的請求處理完成後,再退出。
  • Nginx 在 0.8 版本之後,引入了一系列命令行參數,來方便我們管理。比如,./nginx -s reload,就是來重啟 Nginx,./nginx -s stop,就是來停止 Nginx 的運行。如何做到的呢?我們還是拿 reload 來說,我們看到,執行命令時,我們是啟動一個新的 Nginx 進程,而新的 Nginx 進程在解析到 reload 參數後,就知道我們的目的是控制 Nginx 來重新加載配置文件了,它會向 master 進程發送信號,然後接下來的動作,就和我們直接向 master 進程發送信號一樣了。

    1. worker 進程又是如何處理請求的呢?前面有提到,worker 進程之間是平等的,每個進程,處理請求的機會也是一樣的。當我們提供 80 端口的 http 服務時,一個連接請求過來,每個進程都有可能處理這個連接,怎麽做到由一個worker來處理呢?
      首先,每個 worker 進程都是從 master 進程 fork 過來,在 master 進程裏面,先建立好需要 listen 的 socket(listenfd)之後,然後再 fork 出多個 worker 進程。所有 worker 進程的 listenfd 會在新連接到來時變得可讀,為保證只有一個進程處理該連接,所有 worker 進程在註冊 listenfd 讀事件前搶 accept_mutex,搶到互斥鎖的那個進程註冊 listenfd 讀事件,在讀事件裏調用 accept 接受該連接。當一個 worker 進程在 accept 這個連接之後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開連接,這樣一個完整的請求就是這樣的了。我們可以看到,一個請求,完全由 worker 進程來處理,而且只在一個 worker 進程中處理。
    2. Nginx 采用這種進程模型有什麽好處呢?當然,好處肯定會很多了。首先,對於每個 worker 進程來說,獨立的進程,不需要加鎖,所以省掉了鎖帶來的開銷,同時在編程以及問題查找時,也會方便很多。其次,采用獨立的進程,可以讓互相之間不會影響,一個進程退出後,其它進程還在工作,服務不會中斷,master 進程則很快啟動新的 worker 進程。當然,worker 進程的異常退出,肯定是程序有 bug 了,異常退出,會導致當前 worker 上的所有請求失敗,不過不會影響到所有請求,所以降低了風險。當然,好處還有很多,大家可以慢慢體會。
    3. Nginx 采用多 worker 的方式來處理請求,每個 worker 裏面只有一個主線程,那能夠處理的並發數很有限啊,多少個 worker 就能處理多少個並發,何來高並發呢?非也,這就是 Nginx 的高明之處,Nginx 采用了異步非阻塞的方式來處理請求,也就是說,Nginx 是可以同時處理成千上萬個請求的。想想 apache 的常用工作方式(apache 也有異步非阻塞版本,但因其與自帶某些模塊沖突,所以不常用),每個請求會獨占一個工作線程,當並發數上到幾千時,就同時有幾千的線程在處理請求了。這對操作系統來說,是個不小的挑戰,線程帶來的內存占用非常大,線程的上下文切換帶來的 cpu 開銷很大,自然性能就上不去了,而這些開銷完全是沒有意義的
      9.為什麽 Nginx 可以采用異步非阻塞的方式來處理呢,或者異步非阻塞到底是怎麽回事呢?在 Nginx 裏面,最忌諱阻塞的系統調用了。不要阻塞,那就非阻塞嘍。非阻塞就是,事件沒有準備好,馬上返回 EAGAIN,告訴你,事件還沒準備好呢,你慌什麽,過會再來吧。好吧,你過一會,再來檢查一下事件,直到事件準備好了為止,在這期間,你就可以先去做其它事情,然後再來看看事件好了沒。雖然不阻塞了,但你得不時地過來檢查一下事件的狀態,你可以做更多的事情了,但帶來的開銷也是不小的。所以,才會有了異步非阻塞的事件處理機制,具體到系統調用就是像 select/poll/epoll/kqueue 這樣的系統調用。它們提供了一種機制,讓你可以同時監控多個事件,調用他們是阻塞的,但可以設置超時時間,在超時時間之內,如果有事件準備好了,就返回。
      10.知道了 Nginx 為什麽會選擇這樣的進程模型與事件模型了。對於一個基本的 Web 服務器來說,事件通常有三種類型,網絡事件、信號、定時器

    Nginx 啟動原理與模型