1. 程式人生 > >Tomcat多執行緒模型淺析

Tomcat多執行緒模型淺析

我們在上一部分文章裡已經看到了,Tomcat的架構是如何一步步構建出來,但是在後臺伺服器的構建中,一個很重要的問題是如何實現多執行緒?一般情況下,如果我們來實現最初步的想法就是:不斷迴圈接收客戶端的連線,每個連線構建一個執行緒,然後進行相關的資料處理!但是,實際上我們應該考慮的更多,比如如何選用BIO還是NIO等等?所以,我們來借鑑一下Tomcat的多執行緒設計思路!

首先Tomcat的設計了專門的連線執行緒,即所有的客戶端連線都通過這類執行緒來處理,一旦建立連線就把獲得的客戶端socket交由輪詢執行緒來處理,輪詢各個客戶端後再對已經準備好的客戶端構建一個執行緒,交由執行緒池來處理Request和Response!如果你看原始碼將會發現實際上執行緒模型的基礎AbstractEndpoint,每一種實現包含一種連線、和資料處理方式,我們以NioEndpoint為例,它包含LimitLatch、Acceptor、Poller、SocketProcessor、Excutor5個部分。LimitLatch是連線控制器,它負責維護連線數的計算,nio模式下預設是10000,達到這個閾值後,就會拒絕連線請求。Acceptor負責接收連線,預設是1個執行緒來執行,將請求的事件註冊到事件列表。有Poller來負責輪詢,Poller執行緒數量是cpu的核數Math.min(2,Runtime.getRuntime().availableProcessors())。由Poller將就緒的事件生成SocketProcessor同時交給Excutor去執行。在Excutor的執行緒中,會完成從socket中讀取http request,解析成HttpServletRequest物件,分派到相應的servlet並完成邏輯,然後將response通過socket發回client。一個網上廣泛引用的圖例如下圖:
這裡寫圖片描述


假設一共有四個客戶端連線到了伺服器並請求資料,那麼這個執行緒模型是怎麼工作的呢?我們以下圖為例,作為對上述抽象出來的模型的解說:
這裡寫圖片描述
1、首先主執行緒通過NIO模式建立伺服器繫結,並採用阻塞的模式
2、然後生成Acceptor執行緒,這裡假設就一個執行緒,則這個執行緒將執行while迴圈監聽客戶端的連線請求;
3、啟動Poller執行緒(實際上Poller和Acceptor執行緒幾乎同時啟動,因為多執行緒在未做同步的時候就是預設是非同步的,當Acceptor.start執行之後,可以立即執行Poller.start),假設是雙核處理器,則我們將生成兩個Poller執行緒;如果連線成功,則立即把已經連線的客戶端socket註冊到隨機Poller中,同時NioChannel物件封裝在一個PollerEvent物件中,並將PollerEvent物件壓入events queue裡。這裡是個典型的生產者-消費者模式,Acceptor與Poller執行緒之間通過queue通訊,Acceptor是events queue的生產者,Poller是events queue的消費者。圖例展示的是client1、client2被隨機註冊到Poller1的執行緒中;client3、client4被隨機註冊到Poller2執行緒中;這裡所說的註冊實際上是註冊到Poller物件維護的Events佇列中,然後在通過NIO模型中的”註冊”註冊到select選擇器中,這裡一個Poller維護一個select並對select註冊的NioChannel發起輪詢,凡是準備好的Channel則交由Thread_pool執行緒池;
4、線上程池中,執行緒拿到Poller傳過來的socket後,將socket封裝在SocketProcessor物件中。然後從Http11ConnectionHandler中取出Http11NioProcessor物件,從Http11NioProcessor中呼叫CoyoteAdapter的邏輯,跟BIO實現一樣。在Worker執行緒中,會完成從socket中讀取http request,解析成HttpServletRequest物件,分派到相應的servlet並完成邏輯,然後將response通過socket發回client。