动态

详情 返回 返回

Netty源碼解析-請求處理與多路複用 - 动态 详情

NioEventLoop是什麼?

如圖,NioEventLoop是worker threads中的thread,也就是處理請求的線程,屬於NioEventLoopGroup,那麼多個線程每次選擇哪個線程來處理請求呢?

1.1 Netty給Channel分配Nio Event Loop的規則

看下圖,EventLoopGroup是線程組,每個EventLoop是一個線程,那麼線程處理請求是怎麼分配的呢?我們看一下源碼

1.1.1 我們通過 MultithreadEventLoopGroup.register方法定位到next()方法。

該方法負責NioEventLoop。

1.1.2 進入super.next()方法,我們看到MultithreadEventExecutorGroup的屬性chooser

點擊chooser,我們找到它賦值的地方在構造方法裏面,chooser的正是通過DefaultEventExecutorChooserFactory的newChooser獲取的,

進入newChooser方法,根據傳入的executors長度是否2的指數返回不同的實現類,該類實現了一個策略模式。

注意到這裏isPowerOfTwo只有一行代碼,為什麼還有單獨寫一個方法呢,雖然只有一行代碼但是不容易看懂,通過方法名我們一下就看懂了,這是一種可讀性更好的實現方式

1.1.3 我們繼續看chooser.next()方法,

有兩個實現類GenericEventExecutorChooser和PowerOfTwoEventExecutorChooser,都是在DefaultEventExecutorChooserFactory中。

看兩個實現類實現的有何不同

兩者都是通過idx來獲取索引值的,idx是遞增的,這種模式類似輪詢的方式。但是idx一直遞增下去的話,可能會整數類型的越界,

為了防止越界兩者做了不同的處理

  • GenericEventExecutorChooser,idx對executors.length取模,並取絕對值
  • PowerOfTwoEventExecutorChooser, idx對executors.length - 1做與運算確定位置,這種方式類似於HashMap中對桶位置運算的處理,與運算比數學運算快很多,但是隻能在executors.length是2的指數的時候才可以發揮作用。
例如: executors.length = 2^4, 其二進制為10000,executors.length-1=01111。任何值與1111與運算都不會超過executors.length,同時任何小於executors.length的值和1111與運算都是其本身。這是這個特性才能實現這樣的優化。

這裏PowerOfTwoEventExecutorChooser對next方法做了優化,所以在長度是2的指數的時候我們可以更快的處理

1.1.4 總結

  • 事件處理EventLoop的分配方式是輪詢的,通過記錄調用次數對EventLoop的數量取餘來確定,這種方式能比較好的保證負載均衡
  • 同時根據EventLoop數量是否2的指數做了優化

1.2 多路複用怎麼跨平台的

在不同的系統平台上,EventLoop如何跨平台呢?我們看一下源碼

1.2.1 我 們看一下new NioEventLoopGroup()

點進這個方法,繼續進入,我們找到下面這個方法,看Selector來自SelectorProvider.provider

進入SelectorProvider.provider,這裏的run方法裏面有三個分支。

  • 第一個是從配置的類加載provider
  • 第二個是從系統加載provider
  • 如果前兩個都沒有,看第三個sun.nio.ch.DefaultSelectorProvider.create,這個方法是jre虛擬機的方法,不同平台的jdk版本上,該方法不一樣,

Selector通過這種方式來實現跨平台

下圖是openjdk不同平台是Selector實現:

1.2.2 總結

我們可以看到跨平台是通過不同平台的jvm實現的,不同jvm的平台有不同的實現類,在不同的平台會調用jvm的實現類。

Add a new 评论

Some HTML is okay.