Netty的核心組件的設計都很模塊化,如果想要實現一個應用程序,就需要將這些組件組裝到一起。Netty通過Bootstrap類,以對一個Netty應用程序進行配置(組裝各個組件),並最終使它運行起來。對於客戶端程序和服務器程序所使用到的Bootstrap類是不同的,后者需要使用ServerBootstrap,這樣設計是因為,在如TCP這樣有連接的協議中,服務器程序往往需要一個以上的Channel,通過父Channel來接受來自客戶端的連接,然后創建子Channel用於它們之間的通信,而像UDP這樣無連接的協議,它不需要每個連接都創建子Channel,只需要一個Channel即可。
一個比較明顯的差異就是Bootstrap與ServerBootstrap的group()方法,后者提供了一個接收2個EventLoopGroup的版本。
Bootstrap |
ServerBootstrap |
|
網絡編程中的作用 |
連接到遠程主機和端口 |
綁定到一個本地端口 |
EventLoopGroup 的數目 |
1 |
2 |
一、AbstractBootstrap類
AbstractBootstrap是一個工具類,用於服務器通道的一系列配置,綁定NioEventLoopGroup線程組、指定NIO的模式、指定子處理器,用於處理workerGroup、指定端口等。其有ServerBootstrap、Bootstrap兩個具體的實現。
總的來說:
1、提供了一個ChannelFactory對象用來創建Channel,一個Channel會對應一個EventLoop用於IO的事件處理,在一個Channel的整個生命周期中
只會綁定一個EventLoop,這里可理解給Channel分配一個線程進行IO事件處理,結束后回收該線程。 2、AbstractBootstrap沒有提供EventLoop而是提供了一個EventLoopGroup,上篇博客講過EventLoopGroup對象就是一個含有EventLoop的數組。 但是當一個連接到達,Netty會注冊一個Channel,然后EventLoopGroup會分配一個EventLoop綁定到這個channel。 3、不管是服務器還是客戶端的Channel都需要綁定一個本地端口這就有了SocketAddress類的對象localAddress。 4、Channel有很多選項所有有了options對象LinkedHashMap<channeloption<?>, Object> 5、怎么處理Channel的IO事件呢,我們添加一個事件處理器ChannelHandler對象。
關於AbstractBootstrap類,其代碼如下:
public abstract class AbstractBootstrap<B extends io.netty.bootstrap.AbstractBootstrap<B, C>, C extends Channel> implements Cloneable { /** * 這里的 EventLoopGroup 作為服務端 Acceptor 線程,負責處理客戶端的請求接入 * 作為客戶端 Connector 線程,負責注冊監聽連接操作位,用於判斷異步連接結果。 */ volatile EventLoopGroup group; /** * 創建Channer 工廠 根據傳入的類型來創建不同的Channel * 比如服務器傳入的是:NioServerSocketChannel.class * 客戶端傳入:NioSocketChannel.class 。 加上這個注解代表這個已經過期有更好的替代類 */ @SuppressWarnings("deprecation") private volatile ChannelFactory<? extends C> channelFactory; /** * SocketAddress 是用來綁定一個服務端口 用的 */ private volatile SocketAddress localAddress; /** * ChannelOption 可以添加Channel 添加一些配置信息 */ private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>(); /** * ChannelHandler 是具體怎么處理Channel 的IO事件。 */ private volatile ChannelHandler handler; /** * 傳入一個EventLoopGroup,不管服務端還是客戶端都會調用該方法 */ public B group(EventLoopGroup group) { if (group == null) { throw new NullPointerException("group"); } if (this.group != null) { throw new IllegalStateException("group set already"); } this.group = group; return self(); } /** * 返回對象本身 */ @SuppressWarnings("unchecked") private B self() { return (B) this; } /** *設置服務端的Channel,Netty通過Channel工廠類創建不同的Channel。 * 對於服務端傳入:Netty需要創建NioServerSocketChannel * 對於客戶端傳入:NioSocketChannel.class */ public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); } /** * 創建好Channel后,返回對象本身 */ @Deprecated public B channelFactory(ChannelFactory<? extends C> channelFactory) { if (channelFactory == null) { throw new NullPointerException("channelFactory"); } if (this.channelFactory != null) { throw new IllegalStateException("channelFactory set already"); } this.channelFactory = channelFactory; return self(); } /** * 設置一些Channel相關參數 */ public <T> B option(ChannelOption<T> option, T value) { if (option == null) { throw new NullPointerException("option"); } if (value == null) { synchronized (options) { options.remove(option); } } else { synchronized (options) { options.put(option, value); } } return self(); } /** * 服務端方法: 綁定端口 對該端口進行監聽 */ public ChannelFuture bind(int inetPort) { return bind(new InetSocketAddress(inetPort)); } /** * 客戶端方法: 需要傳入訪問的地址和端口 */ public ChannelFuture bind(String inetHost, int inetPort) { return bind(SocketUtils.socketAddress(inetHost, inetPort)); } public ChannelFuture bind(SocketAddress localAddress) { if (localAddress == null) { throw new NullPointerException("localAddress"); } //這個方法這里省略調,具體可以看源碼 return doBind(localAddress); } /** * 設置父類的Handler,父類的handler是客戶端新接入的接連SocketChannel對應的ChannelPipeline 的handler */ public B handler(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler; return self(); } }
二、Bootstrap類
Bootstrap 是 Netty 提供的一個便利的工廠類, 我們可以通過它來完成 Netty 的客戶端或服務器端的 Netty 初始化。Bootstrap
: 用於客戶端,只需要一個單獨的Channel,來與服務端進行數據交互,對應server端的子Channel。
作用職責
:EventLoop初始化, channel的注冊過程, 關於pipeline的初始化, handler的添加過程, 客戶端連接分析。
如Demo中代碼所示:
EventLoopGroup worker = new NioEventLoopGroup(); try { // 客戶端啟動類程序 Bootstrap bootstrap = new Bootstrap(); /** *EventLoop的組 */ bootstrap.group(worker); /** * 用於構造socketchannel工廠 */ bootstrap.channel(NioSocketChannel.class); /**設置選項 * 參數:Socket的標准參數(key,value),可自行百度 保持呼吸,不要斷氣! * */ bootstrap.option(ChannelOption.SO_KEEPALIVE, true); /** * 自定義客戶端Handle(客戶端在這里搞事情) */ bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new SimpleNettyClientHandler()); } }); /** 開啟客戶端監聽,連接到遠程節點,阻塞等待直到連接完成*/ ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); /**阻塞等待數據,直到channel關閉(客戶端關閉)*/ channelFuture.channel().closeFuture().sync(); } finally { worker.shutdownGracefully(); }
從上面的客戶端代碼展示了 Netty 客戶端初始化時所需的所有內容:
1. EventLoopGroup: 不論是服務器端還是客戶端, 都必須指定 EventLoopGroup. 在這個例子中, 指定了 NioEventLoopGroup, 表示一個 NIO的EventLoopGroup. 2. ChannelType: 指定 Channel 的類型. 因為是客戶端, 因此使用了 NioSocketChannel. 3. Handler: 設置數據的處理器. 4. 這里的option,提供了一系列的TCP參數
深入代碼, 看一下客戶端通過 Bootstrap 啟動后, 都做了哪些工作。
1、group(group)
/** * 直接調用父類AbstractBootstrap的方法 */ public B group(EventLoopGroup group) { if (group == null) { throw new NullPointerException("group"); } if (this.group != null) { throw new IllegalStateException("group set already"); } this.group = group; return self(); }
直接調用父類的方法 ,說明該EventLoopGroup,作為客戶端 Connector 線程,負責注冊監聽連接操作位,用於判斷異步連接結果。
2、channel(NioServerSocketChannel.class)
在 Netty 中, Channel是一個Socket的抽象, 它為用戶提供了關於 Socket 狀態(是否是連接還是斷開) 以及對 Socket 的讀寫等操作. 每當 Netty 建立了一個連接后, 都會有一個對應的 Channel 實例。
/** * 同樣也是直接調用父類AbstractBootstrap的方法 */ public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); }
ReflectiveChannelFactory類:
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> { private final Class<? extends T> clazz; /** * 通過構造函數 傳入 clazz */ public ReflectiveChannelFactory(Class<? extends T> clazz) { if (clazz == null) { throw new NullPointerException("clazz"); } this.clazz = clazz; } /** * 只用這一個方法 通過傳入不同的Channel.class 創建不同的Channel 對象。 * newChannel() 什么時候調用呢 仔細追源碼 發現是在綁定 IP 和 端口的 doResolveAndConnect方法里會調用 */ @Override public T newChannel() { try { return clazz.getConstructor().newInstance(); } catch (Throwable t) { throw new ChannelException("Unable to create Channel from class " + clazz, t); } }
再看channelFactory(new ReflectiveChannelFactory(channelClass)) 方法:
/** * 創建好Channel后,返回對象Bootstrap本身 */ @Deprecated public B channelFactory(ChannelFactory<? extends C> channelFactory) { if (channelFactory == null) { throw new NullPointerException("channelFactory"); } if (this.channelFactory != null) { throw new IllegalStateException("channelFactory set already"); } this.channelFactory = channelFactory; return self(); }
因此對於我們這個例子中的客戶端的 Bootstrap 而言, 生成的的 Channel 實例就是 NioSocketChannel。
Channel 類型
除了 TCP 協議以外, Netty 還支持很多其他的連接協議, 並且每種協議還有 NIO(異步 IO) 和 OIO(Old-IO, 即傳統的阻塞 IO) 版本的區別. 不同協議不同的阻塞類型的連接都有不同的 Channel 類型與之對應,下面是一些常用的 Channel 類型:
- NioSocketChannel, 代表異步的客戶端 TCP Socket 連接. - NioServerSocketChannel, 異步的服務器端 TCP Socket 連接. - NioDatagramChannel, 異步的 UDP 連接 - NioSctpChannel, 異步的客戶端 Sctp 連接. - NioSctpServerChannel, 異步的 Sctp 服務器端連接. - OioSocketChannel, 同步的客戶端 TCP Socket 連接. - OioServerSocketChannel, 同步的服務器端 TCP Socket 連接. - OioDatagramChannel, 同步的 UDP 連接 - OioSctpChannel, 同步的 Sctp 服務器端連接. - OioSctpServerChannel, 同步的客戶端 TCP Socket 連接.
3、handler(ChannelHandler handler)
Netty 的一個強大和靈活之處就是基於 Pipeline 的自定義 handler 機制
. 基於此, 我們可以像添加插件一樣自由組合各種各樣的 handler 來完成業務邏輯. 例如我們需要處理 HTTP 數據, 那么就可以在 pipeline 前添加一個 Http 的編解碼的 Handler, 然后接着添加我們自己的業務邏輯的 handler, 這樣網絡上的數據流就向通過一個管道一樣, 從不同的 handler 中流過並進行編解碼, 最終在到達我們自定義的 handler 中。
/** * 同樣也是 直接調用父類 AbstractBootstrap 的方法 */ public B handler(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler; return self(); }
常用寫法為使用ChannelInitializer進行channel配置:
.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new EchoClientHandler()); } })
那是因為 Bootstrap.handler() 方法接收一個 ChannelHandler, 而我們傳遞的是一個 派生於 ChannelInitializer 的匿名類, 它正好也實現了 ChannelHandler 接口. 我們來看一下, ChannelInitializer 類部分代碼:
/** * ChannelInboundHandlerAdapter 父類的父類 最終會繼承 ChannelHandler * 那么ChannelInitializer 也就是 ChannelHandler的 子類 */ public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter { private static final InternalLogger logger =InternalLoggerFactory.getInstance(ChannelInitializer.class); /** * 這里只有這一個抽象類 所以我們只需重寫這一個方法就可以了 */ protected abstract void initChannel(C ch) throws Exception; @Override @SuppressWarnings("unchecked") public final void channelRegistered(ChannelHandlerContext ctx) throws Exception { initChannel((C) ctx.channel()); ctx.pipeline().remove(this); // 移除自身 ctx.fireChannelRegistered(); } }
ChannelInitializer 是一個抽象類, 它有一個抽象的方法 initChannel
, 我們正是實現了這個方法, 並添加的自定義的 handler 的. 那么 initChannel 是哪里被調用的呢?
答案是 ChannelInitializer.channelRegistered 方法中。
我們來關注一下 channelRegistered 方法. 從上面的源碼中, 我們可以看到, 在 channelRegistered 方法中, 會調用 initChannel 方法, 將自定義的 handler 添加到 ChannelPipeline 中, 然后調用 ctx.pipeline().remove(this) 將自己從 ChannelPipeline 中刪除. 上面的分析過程, 可以用如下圖片展示:
一開始, ChannelPipeline 中只有三個 handler:head, tail 和我們添加的 ChannelInitializer。
接着 initChannel 方法調用后, 添加了自定義的 handler:
最后將 ChannelInitializer 刪除,僅剩下head、tail以及自己添加的handler:
4、ChannelPipeline對象
/** * 我們在initChannel抽象方法的實現方法中 通過 SocketChannel獲得 ChannelPipeline對象 */ ChannelPipeline p = ch.pipeline(); p.addLast(newEchoClientHandler());
在實例化一個 Channel 時, 會伴隨着一個 ChannelPipeline 的實例化
, 並且此 Channel 會與這個 ChannelPipeline 相互關聯, 這一點可以通過NioSocketChannel 的父類 AbstractChannel 的構造器:
protected AbstractChannel(Channel parent) { this.parent = parent; unsafe = newUnsafe(); //這個可以看出 pipeline = new DefaultChannelPipeline(this); }
當實例化一個 Channel(這里以 SimpleNettyClient 為例, 那么 Channel 就是 NioSocketChannel), 其 pipeline 字段就是我們新創建的 DefaultChannelPipeline 對象, 那么我們就來看一下 DefaultChannelPipeline 的構造方法。
public DefaultChannelPipeline(AbstractChannel channel) { if (channel == null) { throw new NullPointerException("channel"); } this.channel = channel; tail = new TailContext(this); head = new HeadContext(this); head.next = tail; tail.prev = head; }
我們調用 DefaultChannelPipeline 的構造器, 傳入了一個 channel, 而這個 channel 其實就是我們實例化的 NioSocketChannel, DefaultChannelPipeline 會將這個 NioSocketChannel 對象保存在channel 字段中。DefaultChannelPipeline 中, 還有兩個特殊的字段, 即head
和tail
, 而這兩個字段是一個雙向鏈表的頭和尾
. 其實在 DefaultChannelPipeline 中, 維護了一個以 AbstractChannelHandlerContext 為節點的雙向鏈表, 這個鏈表是 Netty 實現 Pipeline 機制的關鍵。
5、.connect(host, port)
經過上面的各種分析后, 我們大致了解了 Netty 初始化時, 所做的工作, 接下來 分析一下客戶端是如何發起 TCP 連接的。
/** * 1、 這里 終於是Bootstrap 自己的方法了。 傳入IP 地址 和 端口號 */ public ChannelFuture connect(String inetHost, int inetPort) { //通過InetSocketAddress 構造函數 綁定 IP地址+端口號 return connect(InetSocketAddress.createUnresolved(inetHost, inetPort)); } /** * 2、上面調用該方法 ,該方法在調用 doResolveAndConnect方法 */ public ChannelFuture connect(SocketAddress remoteAddress) { if (remoteAddress == null) { throw new NullPointerException("remoteAddress"); } validate(); return doResolveAndConnect(remoteAddress, config.localAddress()); } /** * 3、這步 實例化 Channer */ private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) { //注意 這里 initAndRegister()方法就是實例化 Channer 的方法 上面說過 真正獲取Channer 對象 是在這步獲取的 final ChannelFuture regFuture = initAndRegister(); final Channel channel = regFuture.channel(); // 這里省略的 很大一部分邏輯判斷的代碼 return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise()); } /** * 3.1 這里 就開始 調 newChannel() 方法 也就創建了 Channel 對象 */ final ChannelFuture initAndRegister() { Channel channel = null; channel = channelFactory.newChannel(); return regFuture; } /** * 4、在看doResolveAndConnect0方法 * 這一步還是對一些 參數數據 進行校驗 省略了校驗代碼 */ private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) { // 獲取 當前 EventLoop線程 final EventLoop eventLoop = channel.eventLoop(); final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop); final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress); //這一步 才是 連接的關鍵 doConnect(resolveFuture.getNow(), localAddress, promise); return promise; }
在 connect 中, 會進行一些參數檢查后, 最終調用的是 doConnect 方法。
三、ServerBootstrap類
ServerBootstrap可以理解為服務器啟動的工廠類,我們可以通過它來完成服務器端的 Netty 初始化。
作用職責: EventLoop初始化
, channel的注冊過程
, 關於pipeline的初始化
, handler的添加過程
, 服務端連接分析。
來看以下Demo中的源碼:
// 服務器端應用程序使用兩個NioEventLoopGroup創建兩個EventLoop的組,EventLoop這個相當於一個處理線程,是Netty接收請求和處理IO請求的線程。 // 主線程組, 用於接受客戶端的連接,但是不做任何處理,跟老板一樣,不做事 EventLoopGroup bossGroup = new NioEventLoopGroup(); // 從線程組, 當boss接受連接並注冊被接受的連接到worker時,處理被接受連接的流量。 EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // netty服務器啟動類的創建, 輔助工具類,用於服務器通道的一系列配置 ServerBootstrap serverBootstrap = new ServerBootstrap(); /** * 使用了多少線程以及如何將它們映射到創建的通道取決於EventLoopGroup實現,甚至可以通過構造函數進行配置。 * 設置循環線程組,前者用於處理客戶端連接事件,后者用於處理網絡IO(server使用兩個參數這個) * public ServerBootstrap group(EventLoopGroup group) * public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) */ serverBootstrap.group(bossGroup, workerGroup) //綁定兩個線程組 // 用於構造socketchannel工廠 .channel(NioServerSocketChannel.class) //指定NIO的模式 /** * @Description: 初始化器,channel注冊后,會執行里面的相應的初始化方法,傳入自定義客戶端Handle(服務端在這里操作) * @Override protected void initChannel(SocketChannel channel) throws Exception { // 通過SocketChannel去獲得對應的管道 ChannelPipeline pipeline = channel.pipeline(); // 通過管道,添加handler pipeline.addLast("nettyServerOutBoundHandler", new NettyServerOutBoundHandler()); pipeline.addLast("nettyServerHandler", new NettyServerHandler()); } * 子處理器也可以通過下面的內部方法來實現。 */ .childHandler(new ChannelInitializer<SocketChannel>() { // 子處理器,用於處理workerGroup protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new SimpleNettyServerHandler()); } }); // 啟動server,綁定端口,開始接收進來的連接,設置8088為啟動的端口號,同時啟動方式為同步 ChannelFuture channelFuture = serverBootstrap.bind(8088).sync(); System.out.println("server start"); // 監聽關閉的channel,等待服務器 socket 關閉 。設置位同步方式 channelFuture.channel().closeFuture().sync(); } finally { //退出線程組 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
1、group(bossGroup, workerGroup)
跟客戶端明顯的一個區別就是,客戶端只傳入了一個NioEventLoopGroup, 而服務端傳入了兩個。看源碼:
/** * 這里調用的是 ServerBootstrap 類本身的 group 方法 發現傳入的兩個EventLoopGroup * 一個賦值給父類(AbstractBootstrap),另一個賦值給 該對象本身屬性 */ public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { //調用父類的group方法 super.group(parentGroup); if (childGroup == null) { throw new NullPointerException("childGroup"); } if (this.childGroup != null) { throw new IllegalStateException("childGroup set already"); } this.childGroup = childGroup; return this; }
在服務器端的初始化時, 我們設置一個是 bossGroup, 另一個是 workerGroup. 那么這兩個 EventLoopGroup 到底是怎么分工的?
bossGroup 是用於服務端 的 accept 的, 即用於處理客戶端的連接請求。workerGroup 它們負責客戶端連接通道的 IO 操作
。關於 bossGroup 與 workerGroup 的關系, 我們可以用如下圖來展示:
首先, 服務器端 bossGroup 不斷地監聽是否有客戶端的連接, 當發現有一個新的客戶端連接到來時, bossGroup 就會為此連接初始化各項資源,然后從 workerGroup 中選出一個 EventLoop 綁定到此客戶端連接中. 那么接下來的服務器與客戶端的交互過程就全部在此分配的 EventLoop 中了。至於 bossGroup 和 workerGroup 和 channel 如何聯系到一起的,等下面再講bind(host)方法的時候在用源碼展示,因為是通過bind(host)
開始將他們聯系到一起的。
2、channel(NioServerSocketChannel.class)方法
這里傳入的類是NioServerSocketChannel,而客戶端是NioSocketChannel,但他們都是通過類的反射機制獲得類的對象的。同樣真正用到該對象的時候,也是在bind(host)方法里。
3、handler()和childHandler()
跟客戶端比較發現還是有明顯區別的, 和 EventLoopGroup 一樣, 服務器端的 handler 也有兩個, 一個是通過 handler() 方法設置 handler 字段, 另一個是通過childHandler() 設置 childHandler 字段。不過handler()方法並不是必須的,而childHandler()方法是必須調用的
。看代碼:
/**handler(new LoggingHandler(LogLevel.INFO)) * * 我們發現channel方法調用的是父類(AbstractBootstrap)的方法 * 所以這個 handler 字段與 accept 過程有關, 即這個 handler 負責處理客戶端的連接請求 */ public B handler(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler; return self(); } /** 再看childHandler(class) * *很明顯 這個childHandler 方法是屬於ServerBootstrap 本身的方法 * 所以推測: 這個childHandler 就是負責和客戶端的連接的 IO 交互 */ public ServerBootstrap childHandler(ChannelHandler childHandler) { if (childHandler == null) { throw new NullPointerException("childHandler"); } this.childHandler = childHandler; return this; }
4、bind(host)方法
bind(host)才是整個流程的關鍵,前面做得只是初始化了一些netty客戶端運行的對象(可以理解成只是創建了對象,並沒有使用它),但真正用到這些這些對象,還是在bind(host)
方法里。如源碼:
/** * 1、調用父類(AbstractBootstrap)的方法 * <p> * 作用: 根據端口號 創建一個InetSocketAddress對象,用於連接連接服務器 */ public ChannelFuture bind(int inetPort) { return bind(new InetSocketAddress(inetPort)); } /** * 2、繼續調用父類(AbstractBootstrap)的方法 * <p> * 作用: 做一些校驗工作 */ public ChannelFuture bind(SocketAddress localAddress) { validate(); if (localAddress == null) { throw new NullPointerException("localAddress"); } return doBind(localAddress); } /** * 3、繼續調用父類(AbstractBootstrap)的方法 * <p> * 作用: 這個方法做了很多事情 */ private ChannelFuture doBind(final SocketAddress localAddress) { //3、1 具體看下面3、1的代碼部分 final ChannelFuture regFuture = initAndRegister(); final Channel channel = regFuture.channel(); ChannelPromise promise = channel.newPromise(); doBind0(regFuture, channel, localAddress, promise); return promise; } } /** * 3、1 這步做了很多重要的事情 */ final ChannelFuture initAndRegister() { Channel channel = null; //這里終於調用newChannel方法了,這里就是之前BootStrap講的ReflectiveChannelFactory對象的方法,這里的 //channel 對象是NioServerSocketChannel。 channel = channelFactory.newChannel(); //這個方法也太重要了 和handle有關 下面3.1.1 講它 init(channel); //這里的group()獲取的就是bootstrap ,這里面會調用next方法 來循環獲取下一個channel 具體的我就不點進去分析了 //這里group().register(channel) 將 bossGroup 和 NioServerSocketChannel 關聯起來了. ChannelFuture regFuture = config().group().register(channel); return regFuture; } /** * 3.1.1 首先可以看到init的方法在父類(AbstractBootstrap)已經提供,只是子類寫具體實現代碼 */ abstract void init(Channel channel) throws Exception; /** * 我們再來看ServerBootstrap實現了init方法,這里面做了很多事 * 比如workerGroup相關,還有handel相關 */ @Override void init(Channel channel) throws Exception { //通過channel獲得ChannelPipeline,說明每一個channel都會對應一個ChannelPipeline ChannelPipeline p = channel.pipeline(); //這里終於獲得workerGroup 對象 final EventLoopGroup currentChildGroup = childGroup; //這里獲得childHandler對象 final ChannelHandler currentChildHandler = childHandler; final Entry<ChannelOption<?>, Object>[] currentChildOptions; final Entry<AttributeKey<?>, Object>[] currentChildAttrs; p.addLast(new ChannelInitializer<Channel>() { @Override public void initChannel(final Channel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); //獲得handel方法傳入的對象 ChannelHandler handler = config.handler(); //這一步說明 .handler(new LoggingHandler(LogLevel.INFO))方法不是必須要的 //如果你沒有調handler方法也沒有關系 ,因為它會在這路做一層判斷 if (handler != null) { pipeline.addLast(handler); } //到這里線程就開始啟動運行了 發現已經講Channel,ChannelPipeline,workerGroup,childHandler等全部聯系到了一起。 ch.eventLoop().execute(new Runnable() { @Override public void run() { pipeline.addLast(new ServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); } }); }