动态

详情 返回 返回

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

摘要

Netty源碼系列-NioEventLoop

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

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

1.1.1 MultithreadEventLoopGroup.register方法

該方法是EventLoop註冊Channel的方法,進入next()方法,就在下圖最上面的一個方法。此方法負責選擇一個NioEventLoop,進入super.next()方法。

1.1.2 super.next()方法

我們看到這裏調用了MultithreadEventExecutorGroup的屬性chooser的next()方法

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

1.1.3 newChooser方法

進入newChooser方法,根據傳入的executors長度是否2的指數返回不同的實現類,這裏實現了一個策略模式。為什麼要這麼做呢?我們繼續往下看

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

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

該方法來自於接口EventExecutorChooserFactory.EventExecutorChooser,有兩個實現類GenericEventExecutorChooser和PowerOfTwoEventExecutorChooser,都是在DefaultEventExecutorChooserFactory中。

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

兩者都是通過idx來獲取索引值的,idx是遞增的,這種模式類似輪詢的方式。但是獲取位置的方式不一樣

  • GenericEventExecutorChooser,idx對executors.length取模,並取絕對值
  • PowerOfTwoEventExecutorChooser, idx對executors.length - 1做與運算確定位置,這種方式類似於HashMap中對桶位置運算的處理,與運算比數學運算快很多,但是隻能在executors.length是2的指數的時候才可以發揮作用。

為什麼不直接用idx呢
因為idx一直累加下去可能會越界,超出數組長度,所以需要取模或者做與運算的方式控制範圍。

例如: executors.length = 2^4, 其二進制為10000,executors.length-1=01111。任何值與1111與運算都不會超過executors.length,同時任何小於executors.length的值和1111與運算都是其本身。這是這個特性才能實現這樣的優化。

總結

在正常情況下我們還是使用GenericEventExecutorChooser來選擇EventLoop,這裏PowerOfTwoEventExecutorChooser對next方法做了優化,所以在EventLoop組長度是2的指數的時候我們可以更快的處理

1.2 多路複用怎麼跨平台的

如下圖,在不同的系統平台上,EventLoop如何跨平台呢?我們看一下Selector的源碼(其他類似)

1.2.1 我們看一下new NioEventLoopGroup()

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

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

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

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

總結

跨平台通過java虛擬機實現,調用java虛擬機的方法,在不同的平台,該方法由不同的java虛擬機實現,這樣實現跨平台,這蒸是java通過虛擬機實現跨平台的方式。

Add a new 评论

Some HTML is okay.