Nginx的事件處理機制:對於一個基本的web伺服器來說,事件通常有三種類型,網路事件、訊號、定時器。
首先看一個請求的基本流程:建立連線---接收資料---傳送資料 。
再次看系統底層的操作 :上述過程(建立連線---接收資料---傳送資料)在系統底層就是讀寫事件。
1)如果採用阻塞調用的方式,當讀寫事件沒有準備好時,必然不能夠進行讀寫事件,那麼久只好等待,等事件準備好了,才能進行讀寫事件。那麼請求就會被耽擱 。阻塞呼叫會進入核心等待,cpu就會讓出去給別人用了,對單線程的worker來說,顯然不合適,當網路事件越多時,大家都在等待呢,cpu空閒下來沒人用,cpu利用率自然上不去了,更別談高並發了。
2)既然沒有準備好阻塞呼叫不行,那就採用非阻塞方式。非阻塞就是,事件,馬上回到EAGAIN,告訴你,事件還沒準備好呢,你慌什麼,過會再來吧。好吧,你過一會,再來檢查一下事件,直到事件準備好了為止,在這期間,你就可以先去做其它事情,然後再來看看事件好了沒。雖然不阻塞了,但你得不時地過來檢查一下事件的狀態,你可以做更多的事情了,但帶來的開銷也是不小的
小結:非阻塞通過不斷檢查事件的狀態來判斷是否進行讀寫操作,這樣帶來的開銷很大。
3)因此才有了非同步非阻塞的事件處理機制。具體到系統呼叫就是像select/poll/epoll/kqueue這樣的系統呼叫。他們提供了一種機制,讓你可以同時監控多個事件,調用他們是阻塞的,但可以設定超時時間,在超時時間之內,如果有事件準備好了,就返回。這個機制解決了我們上面兩個問題。
以epoll為例:當事件還沒準備好時,就放入epoll(隊列)裡面。如果有事件準備好了,那麼就去處理;如果事件回傳的是EAGAIN,那麼繼續將其放入epoll裡面。從而,只要有事件準備好了,我們就去處理她,只有當所有時間都沒有準備好時,才在epoll裡面等著。這樣,我們就可以並發處理大量的並發了,當然,這裡的並發請求,是指未處理完的請求,線程只有一個,所以同時能處理的請求當然只有一個了,只是在請求間進行不斷地切換而已,切換也是因為非同步事件未準備好,而主動讓出的。這裡的切換是沒有任何代價,你可以理解為循環處理多個準備好的事件,事實上就是這樣的。
4)與多線程的比較:
與多線程相比,這種事件處理方式是有很大的優勢的,不需要創建線程,每個請求佔用的內存也很少,沒有上下文切換,事件處理非常的輕量級。並發數再多也不會導致無謂的資源浪費(上下文切換)。
小結:透過非同步非阻塞的事件處理機制,Nginx實現由進程循環處理多個準備好的事件,從而實現高並發和輕量級。
Nginx特點:1. 跨平台:Nginx 可以在大多數 Unix like OS編譯運行,而且也有Windows的移植版本。
2. 配置異常簡單,非常容易上手。配置風格跟程式開發一樣,神一般的配置
3. 非阻塞、高並發連接:資料複製時,磁碟I/O的第一階段是非阻塞的。官方測試能夠支撐5萬並發連接,在實際生產環境中跑到2~3萬並發連接數.(這得益於Nginx使用了最新的epoll模型)
4.事件驅動:通信機制採用epoll模型,支持更大的並發連接。
5. nginx代理和後端web伺服器間無需長連接;
6. 接收用戶請求是異步的,即先將用戶請求全部接收下來,再一次性發送後後端web伺服器,極大的減輕後端web伺服器的壓力
7. 發送回應訊息時,是邊接收來自後端web伺服器的數據,邊發送給客戶端的
8. 網路依賴型低。 NGINX對網路的依賴程度非常低,理論上講,只要能夠ping通就可以實施負載平衡,而且可以有效區分內網和外網流量
9. 支援伺服器偵測。 NGINX能夠根據應用程式伺服器處理頁面傳回的狀態碼、超時資訊等偵測伺服器是否發生故障,並及時傳回錯誤的請求重新提交到其它節點上
master/worker結構:一個master進程,產生一個或多個worker進程
記憶體消耗小:處理大並發的請求記憶體消耗非常小。在3萬並發連線下,開啟的10個Nginx 進程才消耗150M記憶體(15M*10=150M) 成本低廉:Nginx為開源軟體,可以免費使用。而購買F5 BIG-IP、NetScaler等硬體負載平衡交換器則需要十多萬至幾十萬人民幣
內建的健康檢查功能:如果 Nginx Proxy 後端的某台 Web 伺服器宕機了,不會影響前端存取。
節省頻寬:支援 GZIP 壓縮,可以新增瀏覽器本地快取的 Header 頭。
穩定性高:用於反向代理,宕機的機率微乎其微
nginx是以多進程的方式來工作的,當然nginx也是支持多線程的方式的,只是我們主流的方式還是多進程的方式,也是nginx的預設方式。 nginx採用多進程的方式有諸多好處 .
(1) nginx在啟動後,會有一個master進程和多個worker進程。 master進程主要用來管理worker進程,包含:接收來自外界的訊號,向各worker進程發送訊號,監控worker進程的運作狀態,當worker進程退出後(異常情況下),會自動重新啟動新的worker進程。而基本的網路事件,則是放在worker進程中來處理了 。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程相互之間是獨立的 。一個請求,只可能在一個worker進程中處理,一個worker進程,不可能處理其它進程的請求。
worker進程的數量是可以設定的,一般我們會設定與機器cpu核數一致,這裡面的原因與nginx的進程模型以及事件處理模型是分不開的 。
(2)Master接收到信號以後怎樣進行處理(./nginx -s reload )?
首先master進程在接到信號後,會先重新加載配置文件,然後再啟動新的進程,並向所有老的進程發送訊號,告訴他們可以光榮退休了。新的進程在啟動後,就開始接收新的請求,而老的進程在收到來自master的信號後,就不再接收新的請求,並且在當前進程中的所有未處理完的請求處理完成後,再退出.
(3) worker進程又是如何處理請求的呢?
我們前面有提到,worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。當我們提供80埠的http服務時,一個連接請求過來,每個行程都有可能處理這個連接,怎麼做到的呢?首先,每個worker進程都是從master進程fork過來,在master進程裡面,先建立好需要listen的socket之後,然後再fork出多個worker進程,這樣每個worker進程都可以去accept這個socket(當然不是同一個socket,只是每個行程的這個socket會監控在同一個ip位址與端口,這個在網路協定裡面是允許的)。一般來說,當一個連接進來後,所有在accept在這個socket上面的進程,都會收到通知,而只有一個進程可以accept這個連接,其它的則accept失敗,這是所謂的驚群現象。當然,nginx也不會視而不見,所以nginx提供了一個accept_mutex這個東西,從名字上,我們可以看這是一個加在accept上的一把共享鎖。有了這把鎖之後,同一時刻,就只會有一個進程在accpet連接,這樣就不會有驚群問題了。 accept_mutex是一個可控選項,我們可以顯示地關掉,預設是打開的。當一個worker進程在accept這個連接之後,就開始讀取請求,解析請求,處理請求,產生資料後,再返回給客戶端,最後才斷開連接,這樣一個完整的請求就是這樣的了。我們可以看到,一個請求,完全由worker進程來處理,而且只在一個worker進程中處理。
(4) nginx採用這種進程模型有什麼好處呢?
採用獨立的進程,可以讓彼此之間不會影響,一個進程退出後,其它進程還在工作,服務不會中斷,master進程則很快重新啟動新的worker進程。當然,worker進程的異常退出,肯定是程式有bug了,異常退出,會導致目前worker上的所有請求失敗,不過不會影響到所有請求,所以降低了風險。當然,好處還有很多,大家可以慢慢體會。
(5) nginx採用多worker的方式來處理請求,每個worker裡面只有一個主線程,那能夠處理的並發數很有限啊,多少個worker就能處理多少個並發,何來高並發呢?非也,這就是nginx的高明之處,nginx採用了異步非阻塞的方式來處理請求,也就是說,nginx是可以同時處理成千上萬個請求的.
對於IIS伺服器每個請求會獨佔一個工作線程,當並發數上到幾千時,就同時有幾千的線程在處理請求了。這對作業系統來說,是個不小的挑戰,執行緒帶來的記憶體佔用非常大,執行緒的上下文切換帶來的cpu開銷很大,自然效能就上不去了,而這些開銷完全是沒有意義的。我們之前說過,推薦設定worker的個數為cpu的核數,在這裡就很容易理解了,更多的worker數,只會導致進程來競爭cpu資源了,從而帶來不必要的上下文切換。而且,nginx為了更好的利用多核心特性,提供了cpu親緣性的綁定選項,我們可以將某一個進程綁定在某一個核上,這樣就不會因為進程的切換帶來cache的失效
負載平衡
負載平衡技術在現有網路結構之上提供了一種廉價、有效、透明的方法,來擴展網路設備和伺服器的頻寬、增加、增加、增加、增加量加強網路數據處理能力、提高網路的靈活性和可用性。它有兩方面的意義:首先,大量的並發存取或資料流量分擔到多台節點裝置上分別處理,減少使用者等待回應的時間;其次,單一重負載的運算分擔到多台節點裝置上做並行處理,每個節點設備處理結束後,將結果匯總,回傳給用戶,系統處理能力大幅提升
以上就介紹了Nginx----事件處理機制及進程模型,包含了方面的內容,希望對PHP教程有興趣的朋友有幫助。