首頁 > 軟體

分散式Netty原始碼分析概覽

2022-03-24 22:00:16

伺服器端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和執行一些Runnable任務。

功能1:先來看看註冊Channel

即將Channel註冊到Selector上,由Selector來排程Channel的相關事件,如讀、寫、Accept等事件。

而EventLoopGroup的設計是,它包含多個EventLoop(每一個EventLoop通常內部包含一個執行緒),在執行上述註冊過程中是需要選擇其中的一個EventLoop來執行上述註冊行為,這裡就出現了一個選擇策略的問題,該選擇策略介面是EventExecutorChooser,你也可以自定義一個實現。

從上面可以看到,EventLoopGroup做的工作大部分是一些總體性的工作如初始化上述多個EventLoop、EventExecutorChooser等,具體的註冊Channel還是交給它內部的EventLoop來實現。

功能2:執行一些Runnable任務

EventLoopGroup繼承了EventExecutorGroup,EventExecutorGroup也是EventExecutor的集合,EventExecutorGroup也是掌管著EventExecutor的初始化工作,EventExecutorGroup對於Runnable任務的執行也是選擇內部中的一個EventExecutor來做具體的執行工作。

netty中很多工都是非同步執行的,一旦當前執行緒要對某個EventLoop執行相關操作,如註冊Channel到某個EventLoop,如果當前執行緒和所要操作的EventLoop內部的執行緒不是同一個,則當前執行緒就僅僅向EventLoop提交一個註冊任務,對外返回一個ChannelFuture。

總結:EventLoopGroup含有上述2種功能,它更多的是一個集合,但是具體的功能實現還是選擇內部的一個item元素來執行相關任務。 這裡的內部item元素通常即實現了EventLoop,又實現了EventExecutor,如NioEventLoop等

ChannelPipeline介紹

上述EventLoopGroup可以將一個Channel註冊到內部的一個EventLoop的Selector上,然後對於這個Channel的相關讀寫等事件,Netty專門設計了一個ChannelPipeline來進行處理。每一個Channel都有一個ChannelPipeline來處理該Channel的讀寫等事件。

bind過程

上述serverBootstrap的bind過程如下:

  • 建立出你所指定的NioServerSocketChannel,然後初始化一些Socket方面的引數
  • 為上述Channel的ChannelPipeline設定一個ChannelHandler,該ChannelHandler的作用就是在該Channel成功註冊到Selector上的時候,初始化一些邏輯,即initChannel方法中執行一些邏輯,該邏輯就是向ChannelPipeline中新增一個新的ChannelHandler即ServerBootstrapAcceptor
  • 然後開始將該Channel註冊到上述EventLoopGroup bossGroup中,該EventLoopGroup bossGroup會選擇內部的一個EventLoop來執行實際的註冊行為(這個時候就是當前執行緒和操作的EventLoop不是同一個執行緒,即該過程是非同步提交一個Runnable),一旦註冊完成,就執行上述ChannelHandler的initChannel方法

至此,就完成了整個bind過程。一旦EventLoop內部的Selector檢測到NioServerSocketChannel有新的連線到來的事件,則會交給NioServerSocketChannel的ChannelPipeline來處理,重點就是ChannelPipeline中的上述ServerBootstrapAcceptor,ServerBootstrapAcceptor做如下操作:

  • 1 為新的Channel的ChannelPipeline設定我們上述程式碼中的childHandler指定的ChannelHandler
  • 2 將新的Channel註冊到了上述EventLoopGroup workerGroup中

sync介紹

bind方法返回的是一個ChannelFuture,從上面我們也知道該過程是非同步的,sync方法則是一直等待到該非同步過程結束。

再看下f.channel().closeFuture().sync()這個方法

每一個ChannelFuture都是和一個Channel繫結的,所以可以通過ChannelFuture來獲取對應繫結的Channel物件

每一個Channel物件都有一個CloseFuture closeFuture物件,上述closeFuture方法並不是去執行close方法而是獲取到這個CloseFuture closeFuture物件,然後呼叫它的sync方法即等待這個Future的結束。一般正常情況下是不會呼叫這個Future的結束方法的,只是在上述過程或者其他過程出現問題的時候,如註冊到EventLoop失敗等才會去呼叫這個Feture的結束方法,所以正常情況下主執行緒會一直阻塞在CloseFuture closeFuture的sync方法上。

誤區

上述的bossGroup的建立問題。

我們都知道bossGroup是用來accept連線,然後將連線繫結到workerGroup中的,一般情況下bossGroup設定執行緒數為1即可(基本只能為1),我們同時知道Ractor模型中可以使用多個Acceptor執行緒來執行accept操作,加快accept的速度。

如果你想加快accept的速度,想開啟多執行緒來accept,這時候想設定bossGroup的執行緒數為多個的話,就大錯特錯了,是根本沒效果的。

結合上面的原理,只有在bind埠的時候才會建立一個ServerSocketChannel,然後註冊到bossGroup內部的一個EventLoop中,仍然是單執行緒負責ServerSocketChannel的accept工作,而bossGroup中的多執行緒僅僅是為bind多個埠服務的。

我們來看下tomcat是如何允許多個Acceptor執行緒來執行accept操作的:

  • 1 建立了一個ServerSocketChannel serverSock,並bind到某個埠
  • 2 開啟多個Acceptor執行緒,每個執行緒邏輯都是執行上述serverSock的accept方法

沒有使用Selector來執行accept操作,可以多執行緒並行執行上述serverSock的accept方法。

一旦使用了Selector,基本上就相當於將ServerSocketChannel serverSock繫結到了Selector所線上程上了(Selector不是執行緒安全的,只能在一個執行緒中被排程執行)

4 後續

下一篇就要詳細描述下EventLoopGroup了。

以上就是分散式Netty原始碼分析概覽的詳細內容,更多關於分散式Netty原始碼分析的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com