Netty源碼閱讀之如何將TCP的讀寫操做和指定線程綁定

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

前言

在Netty的線程模型中,對於一個TCP鏈接的讀寫操做,都是由一個單線程完成的,對於剛入門Netty的新手,這徹底顛覆咱們熟知的多線程可以加快處理速度,縮短處理時間的常規思路。
實際上,Netty採用了異步通訊模式,一個IO 線程能夠併發處理N 個客戶端鏈接和讀寫操做,這從根本上解決了傳統同步阻塞IO 一鏈接一線程模型,架構的性能、彈性伸縮能力和可靠性都獲得了極大的提高。多線程

源碼閱讀

Channel 註冊到 Worker 線程組上
register()架構

調用 NioEventLoopGroupnext() 從 Worker 線程組中獲取一個 eventLoop
next()併發

根據線程組個數不一樣,會調用 PowerOfTwoEventExecutorChooser 或者 GenericEventExecutorChoosernext() 方法,若是線程數是 2 的 N 次方,就選用 PowerOfTwoEventExecutorChooser 這個 EventLoop 選擇類,使用位運算提升效率
choose executor異步

調用選取的 eventLoopregister() 方法,能夠看到,將 this 也就是當前 EventLoop 當作參數傳入 promise.channel().unsafe().register() 方法
eventLoop.register()oop

繼續進到 promise.channel().unsafe().register 方法,到這裏,終於將 eventLoop 賦值給了 Channel,即 ChanneleventLoop 創建了綁定關係。
channel - eventloop性能

但Channel還未與線程綁定,繼續往下看,當咱們平時在Handler裏調用 ctx (即 ChannelHandlerContext 類對象)的 write() 時,實際是獲取 ctxexecutor 執行寫操縱事件,若未給 ctx 指定 executor,則 ctx 會使用 對應的 channeleventLoop
ctx.eventLoop()this

執行 eventLoopexecute() 方法
executor.execute()spa

進到 execute() 方法內,先經過調用 inEventLoop() 方法,判斷當前線程是不是 eventLoop 綁定的那個線程
eventLoop.inEventLoop()線程

若是不是,則可能 eventLoop 尚未綁定線程,則調用 startThread 方法建立一個線程
eventLoop.execute()

最終調用 eventLoopdoStartThread() ,由 executor 指定建立線程的任務。
eventLoop.doStartThread()

到此,Channel - EventLoop - Thread 綁定在了一塊兒,同時也能看出多個 Channel 可能綁定到 一個EventLoop上

總結

Netty將一個TCP鏈接和一個固定的線程綁定,不須要進行線程切換以及線程同步,即節省資源又提升吞吐效率,除此以外咱們在閱讀源碼的過程當中,從EventLoop的選取,根據不一樣的線程數,使用不一樣的輪詢器,能夠看出Netty對於高性能的極致追求。

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

相關文章
相關標籤/搜索