Nginx架構模型

架構模型

nginx在啓動後,在unix系統中會以daemon的方式在後臺運行,後臺進程包含一個master進程和多個worker進程。在調試的時候可以手動地關掉後臺模式,讓nginx在前臺運行,並且通過配置讓nginx取消master進程,從而可以使nginx以單進程方式運行。

另外,nginx是以多進程的方式來工作的,當然nginx也是支持多線程的方式的,只是我們主流的方式還是多進程的方式,也是nginx的默認方式。nginx採用多進程的方式有諸多好處。

master進程主要用來管理worker進程,包含:接收來自外界的信號,向各worker進程發送信號,監控worker進程的運行狀態,當worker進程退出後(異常情況下),會自動重新啓動新的worker進程。而基本的網絡事件,則是放在worker進程中來處理了。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。一個請求,只可能在一個worker進程中處理,一個worker進程,不可能處理其它進程的請求。

worker進程的個數是可以設置的,一般我們會設置與機器cpu核數一致,這裏面的原因與nginx的進程模型以及事件處理模型是分不開的。nginx的進程模型,可以由下圖來表示:
這裏寫圖片描述

nginx爲了更好的利用多核特性,提供了cpu親緣性的綁定選項,我們可以將某一個進程綁定在某一個核上,這樣就不會因爲進程的切換帶來cache的失效。

Worker處理請求

  • worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。當我們提供80端口的http服務時,一個連接請求過來,每個進程都有可能處理這個連接。
  • 首先,每個worker進程都是從master進程fork過來,在master進程裏面,先建立好需要listen的socket(listenfd)之後,然後再fork出多個worker進程。
  • 所有worker進程的listenfd會在新連接到來時變得可讀,爲保證只有一個進程處理該連接,所有worker進程在註冊listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個進程註冊listenfd讀事件,在讀事件裏調用accept接受該連接。
  • 當一個worker進程在accept這個連接之後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開連接,這樣一個完整的請求就是這樣的了。

我們可以看到,一個請求,完全由worker進程來處理,而且只在一個worker進程中處理。

雖然每個worker裏面只有一個主線程,但nginx採用了異步非阻塞的方式來處理請求,具體到系統調用就是像select/poll/epoll/kqueue這樣的系統調用。它們提供了一種機制,讓你可以同時監控多個事件,調用他們是阻塞的,但可以設置超時時間,在超時時間之內,如果有事件準備好了,就返回。這種機制正好解決了我們上面的兩個問題,拿epoll爲例(在後面的例子中,我們多以epoll爲例子,以代表這一類函數),當事件沒準備好時,放到epoll裏面,事件準備好了,我們就去讀寫,當讀寫返回EAGAIN時,我們將它再次加入到epoll裏面。這樣,只要有事件準備好了,我們就去處理它,只有當所有事件都沒準備好時,纔在epoll裏面等着。這樣,我們就可以併發處理大量的併發了,當然,這裏的併發請求,是指未處理完的請求,線程只有一個,所以同時能處理的請求當然只有一個了,只是在請求間進行不斷地切換而已,切換也是因爲異步事件未準備好,而主動讓出的。這裏的切換是沒有任何代價,你可以理解爲循環處理多個準備好的事件,事實上就是這樣的。

與多線程相比,這種事件處理方式是有很大的優勢的,不需要創建線程,每個請求佔用的內存也很少,沒有上下文切換,事件處理非常的輕量級。併發數再多也不會導致無謂的資源浪費(上下文切換)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章