前言的前言
伺服器模型涉及到執行緒模式和IO模式,搞清楚這些就能針對各種場景有的放矢。該系列分成三部分:
- 單執行緒/多執行緒阻塞I/O模型
- 單執行緒非阻塞I/O模型
- 多執行緒非阻塞I/O模型,Reactor及其改進
前言
這裡探討的伺服器模型主要指的是伺服器端對I/O的處理模型。從不同維度可以有不同的分類,這裡從I/O的阻塞與非阻塞、I/O處理的單執行緒與多執行緒角度探討伺服器模型。
對於I/O,可以分成阻塞I/O與非阻塞I/O兩大型別。阻塞I/O在做I/O讀寫操作時會使當前執行緒進入阻塞狀態,而非阻塞I/O則不進入阻塞狀態。
對於執行緒,單執行緒情況下由一條執行緒負責所有客戶端連線的I/O操作,而多執行緒情況下則由若干執行緒共同處理所有客戶端連線的I/O操作。
多執行緒非阻塞I/O模型
單執行緒非阻塞I/O模型已經大大提高了機器的效率,而在多核的機器上可以通過多執行緒繼續提高機器效率。最樸實、最自然的做法就是將客戶端連線按組分配給若干執行緒,每個執行緒負責處理對應組內的連線。如圖所示,有4個客戶端訪問伺服器,伺服器將套接字1和套接字2交由執行緒1管理,而執行緒2則管理套接字3和套接字4,通過事件檢測及非阻塞讀寫就可以讓每個執行緒都能高效處理。
最經典的多執行緒非阻塞I/O模型方式是Reactor模式。首先看單執行緒下的Reactor,Reactor將伺服器端的整個處理過程分成若干個事件,例如分為接收事件、讀事件、寫事件、執行事件等。Reactor通過事件檢測機制將這些事件分發給不同處理器去處理。如圖所示,若干客戶端連線訪問伺服器端,Reactor負責檢測各種事件並分發到處理器,這些處理器包括接收連線的accept處理器、讀資料的read處理器、寫資料的write處理器以及執行邏輯的process處理器。在整個過程中只要有待處理的事件存在,即可以讓Reactor執行緒不斷往下執行,而不會阻塞在某處,所以處理效率很高。
基於單執行緒Reactor模型,根據實際使用場景,把它改進成多執行緒模式。常見的有兩種方式:一種是在耗時的process處理器中引入多執行緒,如使用執行緒池;另一種是直接使用多個Reactor例項,每個Reactor例項對應一個執行緒。
Reactor模式的一種改進方式如圖所示。其整體結構基本上與單執行緒的Reactor類似,只是引入了一個執行緒池。由於對連線的接收、對資料的讀取和對資料的寫入等操作基本上都耗時較少,因此把它們都放到Reactor執行緒中處理。然而,對於邏輯處理可能比較耗時的工作,可以在process處理器中引入執行緒池,process處理器自己不執行任務,而是交給執行緒池,從而在Reactor執行緒中避免了耗時的操作。將耗時的操作轉移到執行緒池中後,儘管Reactor只有一個執行緒,它也能保證Reactor的高效。
Reactor模式的另一種改進方式如圖所示。其中有多個Reactor例項,每個Reactor例項對應一個執行緒。因為接收事件是相對於伺服器端而言的,所以客戶端的連線接收工作統一由一個accept處理器負責,accept處理器會將接收的客戶端連線均勻分配給所有Reactor例項,每個Reactor例項負責處理分配到該Reactor上的客戶端連線,包括連線的讀資料、寫資料和邏輯處理。這就是多Reactor例項的原理。
多執行緒非阻塞I/O模式讓伺服器端處理能力得到很大提高,它充分利用機器的CPU,適合用於處理高併發的場景,但它也讓程式更復雜,更容易出現問題。
=============廣告時間===============
公眾號的選單已分為“分散式”、“機器學習”、“深度學習”、“NLP”、“Java深度”、“Java併發核心”、“JDK原始碼”、“Tomcat核心”等,可能有一款適合你的胃口。
鄙人的新書《Tomcat核心設計剖析》已經在京東銷售了,有需要的朋友可以購買。感謝各位朋友。
=========================
歡迎關注: