Netty原始碼分析 (二)----- ServerBootstrap
BootStrap在netty的應用程式中負責引導伺服器和客戶端。netty包含了兩種不同型別的引導:
1. 使用伺服器的ServerBootStrap,用於接受客戶端的連線以及為已接受的連線建立子通道。
2. 用於客戶端的BootStrap,不接受新的連線,並且是在父通道類完成一些操作。
一般服務端的程式碼如下所示:
SimpleServer.java
/** * Created by chenhao on 2019/9/4. */ public final class SimpleServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new SimpleServerHandler()) .childHandler(new SimpleServerInitializer()) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(8888).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
SimpleServerHandler.java
private static class SimpleServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelActive"); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelRegistered"); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerAdded"); } }
SimpleServerInitializer.java
public class SimpleServerInitializer extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast("handler", new SimpleChatServerHandler()); System.out.println("SimpleChatClient:" + ch.remoteAddress()+"連線上"); } }
在上篇博文(Netty原始碼分析 (一)----- NioEventLoopGroup)中 剖析瞭如下的兩行程式碼內部的建構函式中幹了些什麼。
EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup();
具體可以見上篇博文,對於如上的兩行程式碼得到的結論是:
1、 如果不指定執行緒數,則執行緒數為:CPU的核數*2
2、根據執行緒個數是否為2的冪次方,採用不同策略初始化chooser
3、產生nThreads個NioEventLoop物件儲存在children陣列中。
可以理解NioEventLoop就是一個執行緒,執行緒NioEventLoop中裡面有如下幾個屬性:
1、NioEventLoopGroup (在父類SingleThreadEventExecutor中)
2、selector
3、provider
4、thread (在父類SingleThreadEventExecutor中)
更通俗點就是: NioEventLoopGroup就是一個執行緒池,NioEventLoop就是一個執行緒。NioEventLoopGroup執行緒池中有N個NioEventLoop執行緒。
ServerBootstrap類分析
本篇博文將分析如下幾行程式碼裡面做了些什麼。
ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new SimpleServerHandler()) .childHandler(new SimpleServerInitializer()) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true);
ServerBootstrap類的繼承結構如下:
該類的引數,有必要列出:
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>(); private volatile EventLoopGroup childGroup; private volatile ChannelHandler childHandler;
其父類AbstractBootstrap的引數
private volatile EventLoopGroup group; private volatile ChannelFactory<? extends C> channelFactory; private volatile SocketAddress localAddress; private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>(); private volatile ChannelHandler handler;
下面主要看下這個鏈式設定相關的引數。
group(bossGroup, workerGroup)
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { 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; }
即將workerGroup儲存在 ServerBootstrap物件的childGroup屬性上。 bossGroup儲存在ServerBootstrap物件的group屬性上
channel(NioServerSocketChannel.class)
public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new BootstrapChannelFactory<C>(channelClass)); } 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 (B) this; }
函式功能:設定父類屬性channelFactory 為: BootstrapChannelFactory類的物件。其中這裡BootstrapChannelFactory物件中包括一個clazz屬性為:NioServerSocketChannel.class,從如下該類的建構函式中可以明顯的得到這一點。
private static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> { private final Class<? extends T> clazz; BootstrapChannelFactory(Class<? extends T> clazz) { this.clazz = clazz; } @Override public T newChannel() { try { return clazz.newInstance(); } catch (Throwable t) { throw new ChannelException("Unable to create Channel from class " + clazz, t); } } @Override public String toString() { return StringUtil.simpleClassName(clazz) + ".class"; } }
並且BootstrapChannelFactory中提供 newChannel()方法,我們可以看到 clazz.newInstance(),主要是通過反射來例項化NioServerSocketChannel.class
handler(new SimpleServerHandler())
public B handler(ChannelHandler handler) { if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler; return (B) this; }
注意:這裡的handler函式的入參類是我們自己提供的。如下,後面的博文中將會分析這個handler將會在哪裡以及何時被呼叫,這裡只需要記住這一點即可
private static class SimpleServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelActive"); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelRegistered"); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerAdded"); } }
childHandler(new SimpleServerInitializer())
public ServerBootstrap childHandler(ChannelHandler childHandler) { if (childHandler == null) { throw new NullPointerException("childHandler"); } this.childHandler = childHandler; return this; }
由最後一句可知,其實就是講傳入的childHandler賦值給ServerBootstrap的childHandler屬性。
該函式的主要作用是設定channelHandler來處理客戶端的請求的channel的IO。 這裡我們一般都用ChannelInitializer這個類的例項或則繼承自這個類的例項
這裡我是通過新建類SimpleChatServerInitializer繼承自ChannelInitializer。具體的程式碼如下:
public class SimpleChatServerInitializer extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast("handler", new SimpleChatServerHandler()); System.out.println("SimpleChatClient:" + ch.remoteAddress()+"連線上"); } }
我們再看看ChannelInitializer這個類的繼承圖可知ChannelInitializer其實就是繼承自ChannelHandler的
可知,這個類其實就是往pipeline中添加了很多的channelHandler。
配置ServerBootstrap的option
這裡呼叫的是父類的AbstractBootstrap的option()方法,原始碼如下:
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 (B) this; }
其中最重要的一行程式碼就是:
options.put(option, value);
這裡用到了options這個引數,在AbstractBootstrap的定義如下:
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
可知是私有變數,而且是一個Map集合。這個變數主要是設定TCP連線中的一些可選項,而且這些屬性是作用於每一個連線到伺服器被建立的channel。
配置ServerBootstrap的childOption
這裡呼叫的是父類的ServerBootstrap的childOption()方法,原始碼如下:
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) { if (childOption == null) { throw new NullPointerException("childOption"); } if (value == null) { synchronized (childOptions) { childOptions.remove(childOption); } } else { synchronized (childOptions) { childOptions.put(childOption, value); } } return this; }
這個函式功能與option()函式幾乎一樣,唯一的區別是該屬性設定只作用於被acceptor(也就是boss EventLoopGroup)接收之後的channel。
總結
比較簡單哈,主要是將我們提供的引數設定到其相應的物件屬性中去了。 因為後面會用到如下的幾個屬性,因此最好知道下,這些屬性是何時以及在那裡賦值的。
1、group:workerGroup儲存在 ServerBootstrap物件的childGroup屬性上。 bossGroup儲存在ServerBootstrap物件的group屬性上
2、channelFactory:BootstrapChannelFactory類的物件(clazz屬性為:NioServerSocketChannel.class)
3、handler:SimpleServerHandler
4、childHandler
5、option
6、childOp