分布式Netty源碼分析概覽
服務(wù)器端demo
看下一個簡單的Netty服務(wù)器端的例子
public static void main(String[] args){
EventLoopGroup bossGroup=new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap=new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 200)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(80,0,4,0,4));
ch.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));
ch.pipeline().addLast(new TcpServerHandler());
}
});
ChannelFuture f=serverBootstrap.bind(8080).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
先來簡單說說上述遇到的類:
EventLoopGroup介紹
它主要包含2個方面的功能,注冊Channel和執(zhí)行一些Runnable任務(wù)。

功能1:先來看看注冊Channel
即將Channel注冊到Selector上,由Selector來調(diào)度Channel的相關(guān)事件,如讀、寫、Accept等事件。
而EventLoopGroup的設(shè)計是,它包含多個EventLoop(每一個EventLoop通常內(nèi)部包含一個線程),在執(zhí)行上述注冊過程中是需要選擇其中的一個EventLoop來執(zhí)行上述注冊行為,這里就出現(xiàn)了一個選擇策略的問題,該選擇策略接口是EventExecutorChooser,你也可以自定義一個實現(xiàn)。
從上面可以看到,EventLoopGroup做的工作大部分是一些總體性的工作如初始化上述多個EventLoop、EventExecutorChooser等,具體的注冊Channel還是交給它內(nèi)部的EventLoop來實現(xiàn)。
功能2:執(zhí)行一些Runnable任務(wù)
EventLoopGroup繼承了EventExecutorGroup,EventExecutorGroup也是EventExecutor的集合,EventExecutorGroup也是掌管著EventExecutor的初始化工作,EventExecutorGroup對于Runnable任務(wù)的執(zhí)行也是選擇內(nèi)部中的一個EventExecutor來做具體的執(zhí)行工作。
netty中很多任務(wù)都是異步執(zhí)行的,一旦當(dāng)前線程要對某個EventLoop執(zhí)行相關(guān)操作,如注冊Channel到某個EventLoop,如果當(dāng)前線程和所要操作的EventLoop內(nèi)部的線程不是同一個,則當(dāng)前線程就僅僅向EventLoop提交一個注冊任務(wù),對外返回一個ChannelFuture。
總結(jié):EventLoopGroup含有上述2種功能,它更多的是一個集合,但是具體的功能實現(xiàn)還是選擇內(nèi)部的一個item元素來執(zhí)行相關(guān)任務(wù)。 這里的內(nèi)部item元素通常即實現(xiàn)了EventLoop,又實現(xiàn)了EventExecutor,如NioEventLoop等
ChannelPipeline介紹
上述EventLoopGroup可以將一個Channel注冊到內(nèi)部的一個EventLoop的Selector上,然后對于這個Channel的相關(guān)讀寫等事件,Netty專門設(shè)計了一個ChannelPipeline來進行處理。每一個Channel都有一個ChannelPipeline來處理該Channel的讀寫等事件。

bind過程
上述serverBootstrap的bind過程如下:
- 創(chuàng)建出你所指定的NioServerSocketChannel,然后初始化一些Socket方面的參數(shù)
- 為上述Channel的ChannelPipeline配置一個ChannelHandler,該ChannelHandler的作用就是在該Channel成功注冊到Selector上的時候,初始化一些邏輯,即initChannel方法中執(zhí)行一些邏輯,該邏輯就是向ChannelPipeline中添加一個新的ChannelHandler即ServerBootstrapAcceptor
- 然后開始將該Channel注冊到上述EventLoopGroup bossGroup中,該EventLoopGroup bossGroup會選擇內(nèi)部的一個EventLoop來執(zhí)行實際的注冊行為(這個時候就是當(dāng)前線程和操作的EventLoop不是同一個線程,即該過程是異步提交一個Runnable),一旦注冊完成,就執(zhí)行上述ChannelHandler的initChannel方法
至此,就完成了整個bind過程。一旦EventLoop內(nèi)部的Selector檢測到NioServerSocketChannel有新的連接到來的事件,則會交給NioServerSocketChannel的ChannelPipeline來處理,重點就是ChannelPipeline中的上述ServerBootstrapAcceptor,ServerBootstrapAcceptor做如下操作:
- 1 為新的Channel的ChannelPipeline配置我們上述代碼中的childHandler指定的ChannelHandler
- 2 將新的Channel注冊到了上述EventLoopGroup workerGroup中
sync介紹
bind方法返回的是一個ChannelFuture,從上面我們也知道該過程是異步的,sync方法則是一直等待到該異步過程結(jié)束。
再看下f.channel().closeFuture().sync()這個方法
每一個ChannelFuture都是和一個Channel綁定的,所以可以通過ChannelFuture來獲取對應(yīng)綁定的Channel對象
每一個Channel對象都有一個CloseFuture closeFuture對象,上述closeFuture方法并不是去執(zhí)行close方法而是獲取到這個CloseFuture closeFuture對象,然后調(diào)用它的sync方法即等待這個Future的結(jié)束。一般正常情況下是不會調(diào)用這個Future的結(jié)束方法的,只是在上述過程或者其他過程出現(xiàn)問題的時候,如注冊到EventLoop失敗等才會去調(diào)用這個Feture的結(jié)束方法,所以正常情況下主線程會一直阻塞在CloseFuture closeFuture的sync方法上。
誤區(qū)
上述的bossGroup的創(chuàng)建問題。
我們都知道bossGroup是用來accept連接,然后將連接綁定到workerGroup中的,一般情況下bossGroup設(shè)置線程數(shù)為1即可(基本只能為1),我們同時知道Ractor模型中可以使用多個Acceptor線程來執(zhí)行accept操作,加快accept的速度。
如果你想加快accept的速度,想開啟多線程來accept,這時候想設(shè)置bossGroup的線程數(shù)為多個的話,就大錯特錯了,是根本沒效果的。
結(jié)合上面的原理,只有在bind端口的時候才會創(chuàng)建一個ServerSocketChannel,然后注冊到bossGroup內(nèi)部的一個EventLoop中,仍然是單線程負(fù)責(zé)ServerSocketChannel的accept工作,而bossGroup中的多線程僅僅是為bind多個端口服務(wù)的。
我們來看下tomcat是如何允許多個Acceptor線程來執(zhí)行accept操作的:
- 1 創(chuàng)建了一個ServerSocketChannel serverSock,并bind到某個端口
- 2 開啟多個Acceptor線程,每個線程邏輯都是執(zhí)行上述serverSock的accept方法
沒有使用Selector來執(zhí)行accept操作,可以多線程并發(fā)執(zhí)行上述serverSock的accept方法。
一旦使用了Selector,基本上就相當(dāng)于將ServerSocketChannel serverSock綁定到了Selector所在線程上了(Selector不是線程安全的,只能在一個線程中被調(diào)度執(zhí)行)
4 后續(xù)
下一篇就要詳細描述下EventLoopGroup了。
以上就是分布式Netty源碼分析概覽的詳細內(nèi)容,更多關(guān)于分布式Netty源碼分析的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用@value注解取不到application.xml配置文件中的值問題
這篇文章主要介紹了使用@value注解取不到application.xml配置文件中的值問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
spring profile 多環(huán)境配置管理詳解
這篇文章主要介紹了 spring profile 多環(huán)境配置管理詳解的相關(guān)資料,需要的朋友可以參考下2017-01-01
Java實現(xiàn)Socket服務(wù)端與客戶端雙向通信功能
大家好,由于工作上業(yè)務(wù)的需要,在java項目中引入了socket通信,特此記錄一下,用以備份,本文章中的socket通信實現(xiàn)了,服務(wù)端與客戶端的雙向通訊,以及二者之間的心跳通信,服務(wù)端重啟之后,客戶端的自動重連功能,需要的朋友可以參考下2025-04-04
Spring?Cloud?Gateway動態(tài)路由Apollo實現(xiàn)詳解
這篇文章主要為大家介紹了Spring?Cloud?Gateway動態(tài)路由通過Apollo實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10

