參考:http://www.infoq.com/cn/articles/netty-version-upgrade-history-thread-part/
1.netty3.x線程模型
2.netty4.x的線程模型
upstream/inbound:主要包括TCP鏈路建立事件、鏈路激活事件、讀事件、I/O異常事件、鏈路關閉事件等
downstream/outbound:主要包括讀寫事件、連接事件、監聽綁定事件、刷新事件等
inbound的讀事件是I/O線程發起的操作
outbound的讀寫事件是用戶的業務線程發起的操作。比如讀事件,用戶線程告訴I/O線程要讀取數據,此時會調用ChannelHandlerContext 的read()方法->UnSafe.beginRead()->調用Selector的read()方法->channelPipe.channelRead ()->channelPipe. fireChannelRead()->channelPipe.channelReadComplete()設置自動讀取后面的數據。
3. 3.x和4.x的線程模型最大區別:
4.x中ChannelPipeline中的Handler鏈統一由I/O線程串行調度,無論是讀還是寫操作,3.x中的write操作時由業務線程處理Handler鏈。4.x中可以降低線程之間的上下文切換帶來的時間消耗,但是3.x中業務線程可以并發執行Handler鏈。如果有一些耗時的Handler操作會導致4.x的效率低下,但是可以考慮將這些耗時操作放在業務線程最先執行,不放在Handler里處理。由于業務線程可以并發執行,同樣也可以提高效率。
4. 4.x的串行話線程模型設計
一個NioEventLoop聚合了一個多路復用器Selector,因此可以處理成百上千的客戶端連接,Netty的處理策略是每當有一個新的客戶端接入,則從NioEventLoop線程組中順序獲取一個可用的NioEventLoop,當到達數組上限之后,重新返回到0,通過這種方式,可以基本保證各個NioEventLoop的負載均衡。一個客戶端連接只注冊到一個NioEventLoop上,這樣就避免了多個I/O線程去并發操作它。
Netty通過串行化設計理念降低了用戶的開發難度,提升了處理性能。利用線程組實現了多個串行化線程水平并行執行,線程之間并沒有交集,這樣既可以充分利用多核提升并行處理能力,同時避免了線程上下文的切換和并發保護帶來的額外性能損耗。
了解完了Netty 4的串行化設計理念之后,我們繼續看Netty 3線程模型存在的問題,總結起來,它的主要問題如下:
1)Inbound和Outbound實質都是I/O相關的操作,它們的線程模型竟然不統一,這給用戶帶來了更多的學習和使用成本;
2)Outbound操作由業務線程執行,通常業務會使用線程池并行處理業務消息,這就意味著在某一個時刻會有多個業務線程同時操作ChannelHandler,我們需要對ChannelHandler進行并發保護,通常需要加鎖。如果同步塊的范圍不當,可能會導致嚴重的性能瓶頸,這對開發者的技能要求非常高,降低了開發效率;
3)Outbound操作過程中,例如消息編碼異常,會產生Exception,它會被轉換成Inbound的Exception并通知到ChannelPipeline,這就意味著業務線程發起了Inbound操作,即需要將該異常交由I/O線程去處理,從而產生了額外的線程的上下文切換!它打破了Inbound操作由I/O線程操作的模型,如果開發者按照Inbound操作只會由一個I/O線程執行的約束進行設計,則會發生線程并發訪問安全問題。由于該場景只在特定異常時發生,因此錯誤非常隱蔽!一旦在生產環境中發生此類線程并發問題,定位難度和成本都非常大。