【Netty】(6) ---原始碼ServerBootstrap
【Netty】6 ---原始碼ServerBootstrap
之前寫了兩篇與Bootstrap相關的文章,一篇是ServerBootstrap的父類,一篇是客戶端Bootstrap類,部落格地址:
ofollow,noindex" target="_blank">【Netty】原始碼AbstractBootstrap
【Netty】原始碼 Bootstrap所以接下來 有關ServerBootstrap 原始碼的分析,如果上面已經分析過了,就不再陳述。
一、概念
ServerBootstrap可以理解為伺服器啟動的工廠類,我們可以通過它來完成伺服器端的 Netty 初始化。
作用職責: EventLoop初始化
, channel的註冊過程
, 關於pipeline的初始化
, handler的新增過程
,服務端連線分析。
下面也先看下原始碼
// 定義一對執行緒組 // 主執行緒組, 用於接受客戶端的連線,但是不做任何處理,跟老闆一樣,不做事 EventLoopGroup bossGroup = new NioEventLoopGroup(); // 從執行緒組, 老闆執行緒組會把任務丟給他,讓手下執行緒組去做任務 EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // netty伺服器的建立, 輔助工具類,用於伺服器通道的一系列配置 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup)//繫結兩個執行緒組 .channel(NioServerSocketChannel.class)//指定NIO的模式 .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new HelloServerInitializer()); // 子處理器,用於處理workerGroup // 啟動server,並且設定8088為啟動的埠號,同時啟動方式為同步 ChannelFuture channelFuture = serverBootstrap.bind(8088).sync(); // 監聽關閉的channel,設定位同步方式 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)方法
這裡和上一篇說的channl是差不多的,具體原始碼可以看上一篇就可以了,只是這裡傳入的類是NioServerSocketChannel,而客戶端是NioSocketChannel,但他們都是通過類的反射
機制獲得類的物件的。同樣真正用到該物件的時候,也是在bind(host)方法裡。
有關NioServerSocketChannel物件和之前的NioSocketChannel物件本身是沒有講過的。
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; }
有關handler和childHandler在哪個地方會被運用,等下將bind()方法的時候,我們在看他的原始碼。
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 首先可以看到into的方法在父類(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)); } }); } }); }
原始碼部落格推薦
有些原始碼自己也沒有去分析 比如:NioServerSocketChannel物件本身,ChannelPipeline物件。
下面推薦原始碼專題部落格
2、 Netty學習筆記
如果一個人充滿快樂,正面的思想,那麼好的人事物就會和他共鳴,而且被他吸引過來。同樣,一個人老帶悲傷,倒黴的事情也會跟過來。 ——在自己心情低落的時候,告誡自己不要把負能量帶給別人。(大校14)