Nginx 高併發原理解析

關鍵詞:異步,非阻塞,epoll

異步

指同一時間不止一個事件發生,或者說是多個相關事件不等待前一事件完成就發生。計算機技術中,異步一詞用於兩大背景。

異步程序設計更為詳細地闡釋該概念,其編寫的代碼使程序能夠要求一項任務和原先任務一起運行,不需停止任務去等待另一任務完成。第二項任務完成時,原先的任務利用一個約定好的機制得到通知,這樣得知任務完成,可以的話就返回結果。有一些編程技術用來實現異步軟件

阻塞和非阻塞

阻塞和非阻塞通常被用來形容多線程間的相互影響。

也即,當一個線程佔用了臨界區資源,那麼其它需要使用這個資源的線程都必須在這個臨界區上等待。等待會導致線程掛起,這樣就形成了阻塞。如果佔用資源的線程一直沒有釋放資源,那麼其它的線程在這個臨界區上都不能繼續工作。反之,非阻塞表明多個線程之間的執行是不會相互影響的。

異步,非阻塞,epoll

異步,非阻塞,使用了epoll 和大量的底層代碼優化。

阻塞I/O模式下,一個線程只能處理一個流的I/O事件。如果想要同時處理多個流,要麼多進程(fork),要麼多線程(pthread_create),這兩種方法有很低的效率。

(1)採用非阻塞忙輪詢的I/O方式,可以同時處理多個流。

  • 為了避免CPU空轉,可以引進了一個代理(select的代理,或poll的代理)。這個代理比較厲害,可以同時觀察許多流的I/O事件,在空閒的時候,會把當前線程阻塞掉,當有一個或多個流有I/O事件時,就從阻塞態中醒來。
  • 如果沒有I/O事件產生,我們的程序就會阻塞在select處。但是依然有個問題,我們從select那裡僅僅知道了,有I/O事件發生了,但卻並不知道是那幾個流(可能有一個,多個,甚至全部),我們只能無差別輪詢所有流,找出能讀出數據,或者寫入數據的流,對他們進行操作。
  • 使用select,有O(n)的無差別輪詢複雜度,同時處理的流越多,沒一次無差別輪詢時間就越長。
  • epoll 可以理解成event poll,不同於忙輪詢和無差別輪詢,epoll會把哪個流發生了怎樣的I/O事件通知我們。此時我們對這些流的操作都是有意義的。(複雜度降低到了O(1))

(2)epoll 觸發模式

  • EPOLLLT水平觸發模式

只要這個文件描述符還有數據可讀,每次 epoll_wait都會返回它的事件,提醒用戶程序去操作

  • EPOLLET邊緣觸發模式

在它檢測到有 I/O 事件時,通過 epoll_wait 調用會得到有事件通知的文件描述符,對於每一個被通知的文件描述符,如可讀,則必須將該文件描述符一直讀到空,讓 errno 返回 EAGAIN 為止,否則下次的 epoll_wait 不會返回餘下的數據,會丟掉事件。如果ET模式不是非阻塞的,那這個一直讀或一直寫勢必會在最後一次阻塞。

  • “事件”的就緒通知方式

通過epoll_ctl註冊fd,一旦該fd就緒,內核就會採用類似callback的回調機制來激活該fd,epoll_wait便可以收到通知。

Nginx: 採用單線程來異步非阻塞處理請求,不會為每個請求分配cpu和內存資源,節省了大量資源,同時也減少了大量的CPU的上下文切換。所以才使得Nginx支持更高的併發。

管理員可以配置Nginx主進程的工作進程的數量(epoll)

Nginx採用一個master進程,多個woker進程的模式


如果一個server採用一個進程負責一個request的方式,那麼進程數就是併發數。正常情況下,會有很多進程一直在等待中。

Nginx 高併發原理解析

多worker進程模式


  • master進程主要負責收集、分發請求。每當一個請求過來時,master就拉起一個worker進程負責處理這個請求。同時master進程也負責監控woker的狀態,保證高可靠性。
  • woker進程一般設置為跟cpu核心數一致。nginx的woker進程在同一時間可以處理的請求數只受內存限制,可以處理多個請求。

Nginx 的異步非阻塞工作方式正把當中的等待時間利用起來了。在需要等待的時候,這些進程就空閒出來待命了,因此表現為少數幾個進程就解決了大量的併發問題

每進來一個request,會有一個worker進程去處理。但不是全程的處理,處理到什麼程度呢?

處理到可能發生阻塞的地方,比如向上遊(後端)服務器轉發request,並等待請求返回。

這個worker會在發送完請求後,註冊一個事件,如果再有request 進來,他就可以很快再按這種方式處理。而一旦上游服務器返回了,就會觸發這個事件,worker才會來接手,這個request才會接著往下走。

如果對您有用,請多多轉發,謝謝。


分享到:


相關文章: