Netty原始碼解析 -- 服務端啟動過程
阿新 • • 發佈:2020-11-01
本文通過閱讀Netty原始碼,解析Netty服務端啟動過程。
**原始碼分析基於Netty 4.1**
Netty是一個高效能的網路通訊框架,支援NIO,OIO等多種IO模式。通常,我們都是使用NIO模式,該系列文章也是解析Netty下NIO模式的實現。
首先,看一個NIO網路通訊示意圖
![](https://upload-images.jianshu.io/upload_images/3804367-cfdc3a9e11cd6a4e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Netty中NIO網路通訊過程在此基礎上實現,下面來看一下具體實現。
### Channel
首先,看一下Netty中的通道Channel,它代表了一個能完成IO操作的通道,提供read, write, connect, bind等方法。
Channel中維護了一個Unsafe物件,用於完成資料傳輸操作(這類操作通常由IO事件觸發,而不是使用者觸發)。
SocketChannel代表Socket連線的網路通道,面向流,支援讀寫操作。
ServerChannel表示可以監聽新連線的通道,ServerSocketChannel代表實現TCP/IP協議的ServerChannel。
AbstractChannel提供基礎邏輯實現,它維護了Unsafe和ChannelPipeline物件,並委託這兩個物件完成實際工作。同時,它也提供newUnsafe,newChannelPipeline方法給子類構造他們需要的物件。
AbstractUnsafe是AbstractChannel的內部類,實現了register,bind,disconnect等方法的基礎邏輯。
ChannelPipeline可以理解為攔截器連結串列,維護了一個ChannelHandler連結串列,ChannelHandler即具體攔截器,負責邏輯處理。
DefaultChannelPipeline是ChannelPipeline介面的預設實現。Netty中Nio相關的Channel都使用它。
可以這樣理解,Unsafe負責資料傳輸,而ChannelPipeline負責邏輯處理。
AbstractNioChannel實現了NIO基礎邏輯,如維護(jvm)SelectableChannel,(jvm)SelectionKey等物件,還有一個很關鍵的selectionKey,代表關注的NIO事件。
AbstractNioUnsafe是AbstractNioChannel內部類,繼承於AbstractUnsafe,並實現Unsafe另一個子介面NioUnsafe,添加了SelectableChannel相關的方法,如finishConnect,read。
AbstractNioChannel的子類可以分成ServerChannel實現類和SocketChannel實現類。
ServerChannel實現類是AbstractNioMessageChannel,newUnsafe方法返回的NioMessageUnsafe。
NioServerSocketChannel是AbstractNioMessageChannel子類,實現TCP/IP協議。
SocketChannel實現類是AbstractNioByteChannel,newUnsafe方法返回的NioByteUnsafe。
NioSocketChannel是AbstractNioByteChannel子類,實現TCP/IP協議。
Channel各實現類關係如下
![](https://upload-images.jianshu.io/upload_images/3804367-62da311dd98d3038.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Netty中將介面劃分得很細微,最好大家可以按功能層次理解各介面代表含義以及實現類的邏輯。
以免後續看原始碼時混淆各介面功能。
### 服務端啟動
首先簡單瞭解一下EventLoop,可以理解為它負責處理網路事件和非同步任務,後面有對應文章詳細解析。
EventLoopGroup則是一組EventLoop集合,它會將操作委託給其中一個EventLoop處理。
Netty的服務端啟動引導類ServerBootstrap中維護了兩個EventLoopGroup,EventLoopGroup#childGroup和AbstractBootstrap#group。
AbstractBootstrap#group負責管理註冊於其上的ServerChannel,處理這些Channel上發生的Accept事件,並將生成的SocketChannel註冊到EventLoopGroup#childGroup。
EventLoopGroup#childGroup處理這些SocketChannel上發生的Read,Write事件。
為了方便,下文我將AbstractBootstrap#group稱為AcceptGroup,ServerBootstrap#childGroup稱為ReadGroup。
這些設計來自Reactor模式,詳細可以見java.util.concurrent包的作者Doug Lea的[《Scalable IO in Java》](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf)。
AbstractBootstrap#bind -> AbstractBootstrap#doBind
```
private ChannelFuture doBind(final SocketAddress localAddress) {
// #1
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
// #2
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
...
}
}
```
`#1` 初始化及註冊ServerChannel。
initAndRegister方法返回**ChannelFuture**,ChannelFuture繼承了(jvm)Future,代表IO非同步處理結果,並且可以繫結回撥函式,非同步IO處理完成Netty後會觸發這些回撥函式。
我們要有這個意識,**Netty是一個非同步框架**,所有的IO操作都是非同步的(充分利用cpu),IO方法不會等待實際IO操作完成,而是返回ChannelFuture。
待實際IO完成後,Netty再觸發ChannelFuture中的回撥函式處理後續邏輯。
ChannelPromise是一種特殊的ChannelFuture,提供更新操作結果的方法(setSuccess,setFailure方法),一般提供給IO方法作為引數(Unsafe中很多方法都有該引數),IO操作完成後,會呼叫這些方法更新操作結果。
`#2` 註冊完成後,繫結ServerChannel監聽埠。
AbstractBootstrap#initAndRegister
```
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// #1
channel = channelFactory.newChannel();
// #2
init(channel);
} catch (Throwable t) {
...
}
// #3
ChannelFuture regFuture = config().group().register(channel);
// #4
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
```
`#1` 構造ServerChannel
AbstractBootstrap#channelFactory是一個ReflectiveChannelFactory物件,他通過反射生成Channel。
ServerBootstrap#channel方法負責構造ReflectiveChannelFactory,並指定具體的ServerChannel類。
(所以我們要通過該方法指定NioServerSocketChannel.class -- `new ServerBootstrap().channel(NioServerSocketChannel.class)`)
`#2` 初始化ServerChannel,該方法由子類實現
`#3` 註冊Channel到AcceptGroup,注意,`config().group()`返回AcceptGroup。
`#4` 如果IO操作發生了異常,需要關閉Channel。
NioServerSocketChannel#構造方法 -> NioServerSocketChannel#newSocket方法
```
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a server socket.", e);
}
}
```
使用(jvm)SelectorProvider,構造一個(jvm)ServerSocketChannel。
這裡完成了NIO網路通訊第一步。
ServerBootstrap#init
```
void init(Channel channel) throws Exception {
// #1
...
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
fin