Jetty9原始碼剖析 - Connector元件 - ServerConnector
轉載自ph0ly:http://www.ph0ly.com
一、ServerConnector的概念
ServerConnector顧名思義是服務端聯結器,是Jetty容器核心元件,它主要完成客戶端與服務端的連線生命週期管理,能夠處理HTTP,HTTP/2和WebSocket,或SSL協議的連線與通訊
二、繼承體系
ServerConnector繼承了AbstractNetworkConnector,同時AbstractNetworkConnector繼承了AbstractConnector,實現了NetworkConnector,NetworkConnector繼承了Connector,AbstractConnector也實現了Connector介面
三、總體架構
連線接入:通過ServerConnector建立的ServerSocketChannel,每個Acceptor會共享該ServerSocketChannel,每個Acceptor在一個執行緒中迴圈執行ServerSocketChannel.accept,當接受到一個連線後,會觸發ServerConnector的accepted方法,這時候會呼叫ServerConnectorManager(實際是一個SelectorManager)選擇一個ManagedSelector來註冊該SocketChannel讀寫事件,並讓ManagedSekector來建立與該SocketChannel相關的SelectChannelEndPoint和HttpConnection
讀事件:連線接入時已經將該連線的SocketChannel註冊到某一個ManagedSelector上,因此ManagedSelector會在後續輪詢(其實是通過EPC來完成生產的,這裡簡化了模型,方便讀者理解)到該讀事件,並呼叫SelectChannelEndPoint.onSelected來處理讀操作,這時候會回撥HttpConnection.onFillable,然後觸發HttpParser.parse來解析Http協議頭,以及後續的Handler執行
寫事件(由於線太多容易亂,圖上很難畫出,所以這裡用文字描述):和讀事件有些類似,寫事件是在應用程式Servlet執行完成後,呼叫HttpServletResponse的OutputStream.write方法完成資料回寫,這時候會觸發HttpOutput.write,之後觸發HttpTransport(HttpChannel)的send方法,最終觸發到SelectChannelEndPoint.write,最後將資料寫入之前註冊的SocketChannel,這樣資料就回寫到客戶端
四、原始碼分析
1. 建構函式
建構函式有很多,實際就呼叫到這個建構函式,因此這裡分析這一個
第一行呼叫到父類AbstractNetworkConnector,而AbstractNetworkConnector也繼續呼叫父類AbstractConnector的建構函式,後面分析父類的構造方法做了什麼
建立SelectorManager,其實就是呼叫如圖方法,建立ServerConnectorManager,是ServerConnector的內部類
由於ServerConnectorManager也是具有生命週期,因此扔到容器託管
最後設定Acceptor的執行緒的執行優先順序,將Acceptor執行緒調至低優先順序,保證其他執行緒優先執行
從圖中可以看到AbstractConnector實現了絕大部分Connector的邏輯
之前架構圖中已經看到,Executor執行緒池、Scheduler定時器、ByteBufferPool緩衝池,三大元件這裡判斷是否使用者指定了,如果沒指定就使用預設的,如果Executor沒指定,就使用Server帶的執行緒池,也就是QueuedThreadPool(簡稱:qtp),如果Scheduler沒指定,則使用ScheduledExecutorScheduler(Jetty基於JDK的ScheduledThreadPoolExecutor實現的定時器執行緒池),如果ByteBufferPool沒指定,則使用ArrayByteBufferPool,將它們3個扔到Connector容器裡面託管(ArrayByteBufferPool不具有生命週期,因此就是一個IOC控制)
如果使用者指定了多個ConnectionFactory,這裡還要將這些ConnectionFactory新增到該Connector,_factories是一個LinkedHashMap,將不同協議的對映到ConnectionFactory,這裡就不細講了
之後計算Acceptor個數和與之對應的執行緒個數,計算規則是基於,max(1, min(4, JVM的可用處理器數/8)),可以看得出最大就4個,最少1個,使用者也可以自己指定,如果指定了,就以使用者的為準(但是建議不要超過JVM可用處理器數)
2. 啟動(doStart)
第一行呼叫父類,待會兒講解,先來分析getAcceptors如果為0,也就是之前使用者設定了acceptors=0,那就會將這個ServerSocketChannel放到Selector來監聽Accept事件,注意這裡和前面的不一樣,預設都是AbstractConnector裡面的Acceptor來阻塞ServerSocketChannel.accept接受連線,如果不設定acceptors就會使用ManagedSelector來處理連線的接受,這樣的話就將之前文章說的IO模型退化為多執行緒Reactor IO模型
再來看AbstractNetworkConnector.doStart
ServerConnector.open會開一個新的ServerSocketChannel,配置為阻塞模式,並託管IOC容器
ServerConnector.openAcceptChannel其實就是根據使用者的配置來監聽地址和埠
再回來看AbstractNetworkConnector.doStart,呼叫了父類的AbstractConnector.doStart
這裡拿到預設協議的預設連線工廠(預設當然就是Http1.x協議了)
然後拿SslConnectionFactory,如果有就校驗ssl協議的下一個協議,很明顯ssl協議只是安全層,需要真實的後續協議,如Http、或者Http2來支援
之後super.doStart就不講了,生命週期的
_stopping是停止的時候用的,這裡就不細講
之後根據之前配置好的_acceptor個數來建立Acceptor,注意這裡把他們扔到了執行緒池執行,並不是之前我們Thread[]實體執行緒來執行,看起來挺坑的
3. 連線接入
可以看到上面啟動的時候,已經將Acceptor扔到執行緒池執行了,自然這裡的run方法要執行起來
上面初始化的程式碼就不解釋,很簡單,我們直接看while迴圈裡面在幹啥
如果在接受狀態,就會一直執行accept方法,這就是Acceptor唯一要做的事情,再來看看accept怎麼實現的
accept方法調回了ServerConnector.accept方法,如上圖
這裡阻塞呼叫ServerSocketChannel.accept,獲取到一個連線後,呼叫accepted方法
這裡將新的連線配置為非阻塞模式,並配置TCP NoDelay引數,並呼叫SelectorManager來分配這個連線對應的ManagedSelector,並建立該連線的EndPoint和Connection,後續的SelectorManager和ManagedSelector會詳細分析這塊邏輯
如果想現在瞭解SelectorManager,點我
如果想現在瞭解ManagedSelector,點我
4. 資料讀寫與Http處理
由於後面的文章會詳細分析資料讀寫,因此這裡不再講解,請關注後續的文章
HttpConnection
SelectChannelEndPoint
五、總結
總體來說ServerConnector是基於NIO的同步非阻塞IO模型,配合Callbacks來模擬了一套非同步IO的處理框架。它是連線接受的入口,利用SelectorManager分配Selector,但資料讀寫是ManagedSelector來管理,這樣就完成連線與讀寫事件的分離,增強了健壯性及高併發效能。後面會詳細講解SelectorManager連線的分發,以及ManagedSelector對讀寫事件的處理,歡迎大家持續關注~