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)); } }); } }); }