分布式Netty源碼分析概覽
服務器端demo
看下一個簡單的Netty服務器端的例子
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任務。
功能1:先來看看注冊Channel
即將Channel注冊到Selector上,由Selector來調(diào)度Channel的相關(guān)事件,如讀、寫、Accept等事件。
而EventLoopGroup的設計是,它包含多個EventLoop(每一個EventLoop通常內(nèi)部包含一個線程),在執(zhí)行上述注冊過程中是需要選擇其中的一個EventLoop來執(zhí)行上述注冊行為,這里就出現(xiàn)了一個選擇策略的問題,該選擇策略接口是EventExecutorChooser,你也可以自定義一個實現(xiàn)。
從上面可以看到,EventLoopGroup做的工作大部分是一些總體性的工作如初始化上述多個EventLoop、EventExecutorChooser等,具體的注冊Channel還是交給它內(nèi)部的EventLoop來實現(xiàn)。
功能2:執(zhí)行一些Runnable任務
EventLoopGroup繼承了EventExecutorGroup,EventExecutorGroup也是EventExecutor的集合,EventExecutorGroup也是掌管著EventExecutor的初始化工作,EventExecutorGroup對于Runnable任務的執(zhí)行也是選擇內(nèi)部中的一個EventExecutor來做具體的執(zhí)行工作。
netty中很多任務都是異步執(zhí)行的,一旦當前線程要對某個EventLoop執(zhí)行相關(guān)操作,如注冊Channel到某個EventLoop,如果當前線程和所要操作的EventLoop內(nèi)部的線程不是同一個,則當前線程就僅僅向EventLoop提交一個注冊任務,對外返回一個ChannelFuture。
總結(jié):EventLoopGroup含有上述2種功能,它更多的是一個集合,但是具體的功能實現(xiàn)還是選擇內(nèi)部的一個item元素來執(zhí)行相關(guān)任務。 這里的內(nèi)部item元素通常即實現(xiàn)了EventLoop,又實現(xiàn)了EventExecutor,如NioEventLoop等
ChannelPipeline介紹
上述EventLoopGroup可以將一個Channel注冊到內(nèi)部的一個EventLoop的Selector上,然后對于這個Channel的相關(guān)讀寫等事件,Netty專門設計了一個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í)行實際的注冊行為(這個時候就是當前線程和操作的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來獲取對應綁定的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ù)為1即可(基本只能為1),我們同時知道Ractor模型中可以使用多個Acceptor線程來執(zhí)行accept操作,加快accept的速度。
如果你想加快accept的速度,想開啟多線程來accept,這時候想設置bossGroup的線程數(shù)為多個的話,就大錯特錯了,是根本沒效果的。
結(jié)合上面的原理,只有在bind端口的時候才會創(chuàng)建一個ServerSocketChannel,然后注冊到bossGroup內(nèi)部的一個EventLoop中,仍然是單線程負責ServerSocketChannel的accept工作,而bossGroup中的多線程僅僅是為bind多個端口服務的。
我們來看下tomcat是如何允許多個Acceptor線程來執(zhí)行accept操作的:
- 1 創(chuàng)建了一個ServerSocketChannel serverSock,并bind到某個端口
- 2 開啟多個Acceptor線程,每個線程邏輯都是執(zhí)行上述serverSock的accept方法
沒有使用Selector來執(zhí)行accept操作,可以多線程并發(fā)執(zhí)行上述serverSock的accept方法。
一旦使用了Selector,基本上就相當于將ServerSocketChannel serverSock綁定到了Selector所在線程上了(Selector不是線程安全的,只能在一個線程中被調(diào)度執(zhí)行)
4 后續(xù)
下一篇就要詳細描述下EventLoopGroup了。
以上就是分布式Netty源碼分析概覽的詳細內(nèi)容,更多關(guān)于分布式Netty源碼分析的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實現(xiàn)利用圖片或視頻生成GIF并發(fā)送微信
這篇文章主要為大家詳細介紹了Java語言如何利用圖片或視頻實現(xiàn)生成GIF并發(fā)送微信的功能,文中的示例代碼講解詳細,感興趣的小伙伴可以嘗試一下2022-11-11java使用Process調(diào)用exe程序及Process.waitFor()死鎖問題解決
在編寫Java程序時,有時候我們需要調(diào)用其他的諸如exe,shell這樣的程序或腳本,下面這篇文章主要給大家介紹了關(guān)于java使用Process調(diào)用exe程序及Process.waitFor()死鎖問題解決的相關(guān)資料,需要的朋友可以參考下2022-12-12MyBatis自定義TypeHandler如何解決字段映射問題
這篇文章主要介紹了MyBatis自定義TypeHandler如何解決字段映射問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12spring中@autowired、@Qualifier、@Primary注解的使用說明
這篇文章主要介紹了spring中@autowired、@Qualifier、@Primary注解的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11Spring?Boot2.6.0新特性之默認禁止循環(huán)引用
Spring?Boot2.6.0為我們帶來很多好用的新特性/改進,這篇文章主要給大家介紹了關(guān)于Spring?Boot2.6.0新特性之默認禁止循環(huán)引用的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-02-02Java線程池ThreadPoolExecutor的使用及其原理詳細解讀
這篇文章主要介紹了Java線程池ThreadPoolExecutor的使用及其原理詳細解讀,線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然后在創(chuàng)建線程后自動啟動這些任務,線程池線程都是后臺線程,需要的朋友可以參考下2023-12-12