1. 程式人生 > 實用技巧 >Netty原始碼閱讀之如何將TCP的讀寫操作和指定執行緒繫結

Netty原始碼閱讀之如何將TCP的讀寫操作和指定執行緒繫結

原文連結http://xueliang.org/article/detail/20200712234015993

前言

在Netty的執行緒模型中,對於一個TCP連線的讀寫操作,都是由一個單執行緒完成的,對於剛入門Netty的新手,這完全顛覆我們熟知的多執行緒能夠加快處理速度,縮短處理時間的常規思路。

實際上,Netty採用了非同步通訊模式,一個IO 執行緒可以併發處理N 個客戶端連線和讀寫操作,這從根本上解決了傳統同步阻塞IO 一連線一執行緒模型,架構的效能、彈性伸縮能力和可靠性都得到了極大的提升。

原始碼閱讀

Channel 註冊到 Worker 執行緒組上

呼叫 NioEventLoopGroup

next() 從 Worker 執行緒組中獲取一個 eventLoop

根據執行緒組個數不同,會呼叫 PowerOfTwoEventExecutorChooser 或者 GenericEventExecutorChoosernext() 方法,如果執行緒數是 2 的 N 次方,就選用 PowerOfTwoEventExecutorChooser 這個 EventLoop 選擇類,使用位運算提高效率

呼叫選取的 eventLoopregister() 方法,可以看到,將 this 也就是當前 EventLoop 當做引數傳入 promise.channel().unsafe().register()

方法

繼續進到 promise.channel().unsafe().register 方法,到這裡,終於將 eventLoop 賦值給了 Channel,即 ChanneleventLoop 建立了繫結關係。

但Channel還未與執行緒繫結,繼續往下看,當我們平時在Handler裡呼叫 ctx (即 ChannelHandlerContext 類物件)的 write() 時,實際是獲取 ctxexecutor 執行寫操縱事件,若未給 ctx 指定 executor,則 ctx 會使用 對應的 channeleventLoop

執行 eventLoopexecute()

方法

進到 execute() 方法內,先通過呼叫 inEventLoop() 方法,判斷當前執行緒是否是 eventLoop 繫結的那個執行緒

如果不是,則可能 eventLoop 還沒有繫結執行緒,則呼叫 startThread 方法建立一個執行緒

最終呼叫 eventLoopdoStartThread() ,由 executor 指定建立執行緒的任務。

到此,Channel - EventLoop - Thread 繫結在了一起,同時也能看出多個 Channel 可能繫結到 一個EventLoop上

總結

Netty將一個TCP連線和一個固定的執行緒繫結,不需要進行執行緒切換以及執行緒同步,即節省資源又提高吞吐效率,除此之外我們在閱讀原始碼的過程中,從EventLoop的選取,根據不同的執行緒數,使用不同的輪詢器,可以看出Netty對於高效能的極致追求。

原文連結http://xueliang.org/article/detail/20200712234015993