NIO 服務(wù)端 ServerSocketChannel 啟動步驟
- 創(chuàng)建
- 創(chuàng)建 serverSocketChannel璧南,并將其綁定到某個端口,配置為非阻塞突勇。
- 創(chuàng)建 selector悯嗓。
- 注冊
- 將 ServerSocketChannel 注冊到 selector摸柄,并監(jiān)聽 accept 事件。
- 等待連接
- 調(diào)用 selector.select() 方法阻塞碍舍,直到有客戶端連接柠座。
- 處理 accept 事件
- 從 selector.selectedKeys() 方法獲取客戶端連接的 socketChannel,對其進(jìn)行操作片橡。
Netty 是對 Java NIO 的封裝處理妈经,使NIO更容易被使用,但是不管怎么封裝捧书,底層也需要遵循這個步驟來啟動服務(wù)端
Reactor 模式
Doug Lea 在 《Scalable IO in Java》 中定義了三種 Reactor 模式吹泡,分別為
- Basic Reactor
- Multithreaded Reactor
- Multiple Reactors
Basic Reactor
Basic Reactor 如下圖所示:
特點(diǎn):
- 在 Basic Reactor 中,不管是處理 serverSocketChannel 的 accept 事件 還是處理 socketChannel 的 read/write 等事件经瓷,都是在同一個線程中進(jìn)行的
缺點(diǎn):
- 資源利用率低爆哑,一個線程處理所有的請求,無法有效利用CPU
Multithreaded Reactor
Multithreaded Reactor 如下圖所示:
特點(diǎn):
- 當(dāng) selector 返回 serverSocketChannel 的 accept 事件后了嚎,由 main 線程處理該事件
- 當(dāng) selector 返回 socketChannel 的 read/write 事件后泪漂,將該事件讓 ThreadPool 分配的線程來處理
優(yōu)點(diǎn):
- 將 accept 事件和 read/write 事件分開處理,有效利用CPU資源
缺點(diǎn):
- 一個 selector 輪詢所有事件歪泳,當(dāng)需要處理很多socketChannel 的 read/write 事件時,部分 serverSocketChannel 的 accept 會阻塞露筒,未能有效處理客戶端的連接請求
Multiple Reactors
Multiple Reactors 如下圖所示:
特點(diǎn):
- 有兩個 seletcor呐伞,main selector 和 sub selector
- 將 serverSocketChannel 注冊到 main selector 中,并讓 main selctor
監(jiān)聽 accept 事件慎式,當(dāng)返回 accept 事件時伶氢,使用 main 線程進(jìn)行處理 - 將 socketChannle 注冊到 sub selector 中,讓 sub selector 監(jiān)聽 read/write 等事件瘪吏,當(dāng)返回 read/write事件時癣防,將事件給 ThreadPool 分配的線程處理
優(yōu)點(diǎn):
- 當(dāng) server 與很多 client 進(jìn)行讀寫操作時,并不會阻塞 main selector的 accept 事件處理掌眠,使accept 事件能夠得到及時處理
Netty 對 Reactor 模式的支持
Netty 對三種 Reactor 模式都能進(jìn)行處理蕾盯,只需要對 ServeBootstrap 分配不同配置的 NioEventLoopGroup即可
Netty 對 Basic Reactor 的支持
EventLoopGroup group = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(group)
...
- NioEventLoopGroup 其實(shí)是 NioEventLoop 的線程集合,在 NioEventLoopGroup 是包含1個或者多個NioEventLoop 的線程池蓝丙。
- 而在每個 NioEventLoop 中間级遭,包含著一個 seletor,用來監(jiān)聽 Channel 的事件渺尘。
- 在上面代碼中挫鸽,NioEventLoopGroup 的構(gòu)造參數(shù)表示NioEventLoop線程集合的數(shù)量,在這里只分配一個 NioEventLoop鸥跟,所以就只有一個selector丢郊,將所有的Channel 都將注冊到 selector 中,NioEventLoopGroup分配了一個NioEventLoop,則說明只有一個NioEventLoopGroup的線程組里面只有一個線程枫匾,對于所有的channel都由這個線程進(jìn)行處理迅诬,故滿足了 Basic Reactor 的特性。
Netty 對 Multithreaded Reactor 的支持
Netty 并沒有對 Multithreaded Reactor 的支持
因?yàn)樵?Netty 中婿牍,每個 NioEventLoop 都有一個 selector侈贷,SocketChannel/ServerSocketChannel 會被注冊到某一個 NioEventLoop 里面的selector中,當(dāng)這個selector 返回事件時等脂,也只會被該 NioEventLoop 所在的線程進(jìn)行處理俏蛮。
而 NioEventLoopGroup 是 NioEventLoop的集合
所以如果為 NioEventLoopGroup 集合里有多個 NioEventLoop,則也會創(chuàng)建多個selector上遥,每個 NioEventLoop 都會創(chuàng)建一個線程去處理selector 的事件搏屑,做不到一個 selector 監(jiān)聽的事件交給多個線程去處理。
在有些文章中說粉楚,以下代碼就是實(shí)現(xiàn)了Multithreaded Reactor
EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(group)
...
但是進(jìn)去 ServerBootstrap 源碼發(fā)現(xiàn)辣恋,其實(shí)只不過是將 group 集合中抽一個 NioEventLoop 作為 Main Reactor,group 集合里面的其他NioEventLoop 則繼續(xù)作為 Sub Reactor模软,是 Netty 對 Multiple Reactors 的支持伟骨。
@Override
public ServerBootstrap group(EventLoopGroup group) {
// 此方法也是 bossGroup, workerGroup 調(diào)用的方法
return group(group, group);
}
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
Netty 對 Multiple Reactors 的支持
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup wokerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, wokerGroup )
...
- bossGroup 作為 Main Reactor,serverSocketChannel 將注冊accept事件到 bossGroup 里面的 NioEventLoop 中 selector 中燃异,由該 selector 輪訓(xùn) accept 事件携狭。
- workerGroup 作為 Sub Reactor,所有的 socketChannel 會被注冊到workerGroup 里的 NioEventLoop 中 selector,這些 selector 負(fù)責(zé)輪訓(xùn) read 事件回俐,監(jiān)聽到的事件交給對應(yīng)的NioEventLoop 進(jìn)行處理逛腿。
Reactor 和 Selector:
在閱讀 Netty 源碼之前,一直以為 Reactor 就是 Selector仅颇,Multi Reactor (主從Reactor)就是一個 selector 負(fù)責(zé)監(jiān)聽 accept 事件单默,一個 selector 負(fù)責(zé)監(jiān)聽 read 事件,只不過處理 accept 事件的線程只有一個忘瓦,而處理 read 事件的線程有多個搁廓。
但是看了 Netty 源碼后發(fā)現(xiàn)并不是這樣,Reactor 其實(shí)是 selector 的線程集合政冻,一個 Reactor 可以有一個或者多個 selector枚抵,在 Netty 中,Main Reactor 只有一個 selector明场, 而 Sub Reactor 有多個 selector汽摹,每個 selector 互不影響,都只處理在自己中注冊的socketChannel