1. 程式人生 > >Netty原始碼解析 -- 服務端啟動過程

Netty原始碼解析 -- 服務端啟動過程

本文通過閱讀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