io.netty.channel.Channel是Netty网络操作抽象层,聚合了一组功能,包括但不限于网路的读写,客户端发起连接,主动关闭连接,链路关闭,获取通信双方的网络地址等。
Unsafe是内部接口,聚合在Channel中协助进行网络读写相关的操作,因为它涉及初衷就是Channel的内部辅助类,不应该被Netty的上层使用者调用,所以被命名为Unsafe。
一个Channel会绑定一个NioEventLoop线程,绑定一条ChannelPipeline处理器流水线,而ChannelPipeline又维护了一系列的ChannelHandler。
Channle根据协议不同和阻塞方式不同有很多不同实现:
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 连接
其中最常用的就是NioSocketChannel和NioServerSocketChannel,本文以NioServerSocketChannel为例,简述其初始化和运行流程。
程序入口是AbstractBootstrap#bind()方法,上文对NioEventLoop的介绍中,因为重点关注NioEventLoop的启动运行过程,所以跳过了channel的初始化流程,本文详细介绍其启动注册流程:
initAndRegister()方法代码如下:
通过channelFactory反射调用其构造方法创一个channel实例。构造方法如下:
执行newSocket方法:
首先使用SelectorProvider的openServerSocketChannel打开SocketChannel。向上调用最顶级父类AbstractChannel的构造方法:
初始化成员变量,parent表示通道的父通道,对于连接监听通道NioServerSocketChannel来说,父通道为null,对于每一条传输通道NioSocketChannel,其parent属性的值为接收到该连接的服务器连接监听通道,然后构建一个NioMessageUnsafe对象,用于完成实际的I/O操作和传输,再然后创建了一个DefaultChannelPipeline实例,即每个Channel创建时都会分配一个ChannelPipeline维护一系列ChannelHandler。然后继续父类AbstractNioChannel的构造如下:
初始化参数,readInterestOp是上一层传过来的SelectionKey.OP_ACCEPT,然后将ServerSocketChannel设为非阻塞状态。然后再回上一层:
创建ChannleConfig对象,主要是TCP参数配置类。回到initAndRegister(),创建完Channel对象后,调用init进行后续的初始化操作:
首先,设置启动时配置的options和attributes参数,主要是TCP的一些属性和用户自定义的一些参数。然后获取当前Channel绑定的ChannelPipeline,调用其addLast方法将一个ChannelInitializer封装成一个DefaultChannelHandlerContext对象加入到ChannelPipeline维护的ChannelHandler链表尾部(ChannelPipeline相关后续详细介绍)。这个ChannelInitializer重写的run方法中,将配置的handler加入pipeline中,然后新建一个任务将ServerBootstrapAcceptor(继承了 ChannelInboundHandlerAdapter,本身也是一个handler)加入到NioServerSocketChannel所关联的ChannelPipeline中。ServerBootstrapAcceptor主要完成将NioServerSocketChannel所接收到的连接请求NioSocketChannel注册到childGroup中。因为此时Channel还没被注册到EventLoop上,所以会将会将ChannelInitializer封装为PendingHandlerAddedTask任务,并赋值给成员变量pendingHandlerCallbackHead,它会在channel注册到EventLoop时得以调用handlerAdded方法来执行initChannel方法。
init方法执行完后回到initAndRegister()方法,执行ChannelFuture regFuture = config().group().register(channel)完成channnl的注册。获取bossGroup对应的NioEventLoopGroup调用其register方法,入口在MultithreadEventLoopGroup类:
通过选择器选择一个NioEventLoop调用其register方法,入口在SingleThreadEventLoop:
跟踪如下:
unsafe()会返回一个NioMessageUnsafe实例,完整Channel真实的I/O操作和传输。实现如下:
一个channel只能绑定一个NioEventLoop线程,所以依旧是调用inEventLoop()判断当前方法是否在EventLoop所在的线程(此处为false),如果不是就将其封装为一个tack任务提交到taskQueue中,在后续时间循环中执行,如果是,直接执行。注册方法register0()方法如下:
调用doRegister()方法如下:
将当前的ServerSocketChannel注册到NioEventLoop的Selector上,将NioServerSocketChannel作为附加属性设置到SelectionKey中。
然后回到主方法中,将registered置为true,表示已经完成注册,调用pipeline.invokeHandlerAddedIfNeeded()方法,最终会调用callHandlerAddedForAllHandlers方法:
执行上文中封装的PendingHandlerAddedTask任务,即通过ChannelInitializer的handlerAdded方法就会完成将我们自定义的定义的Handler添加到ChannelPipeline中,以及将ServerBootstrapAcceptor添加到ChannelPipeline的操作封装为一个task提交到EventLoop的taskQueue中,随后EventLoop的事件循环就会从taskQueue中取出该任务并执行。
回到主方法继续执行将ChannelPromise设为成功,然后调用pipeline.fireChannelRegistered()触发触发ChannelRegistered事件,在ChannelPipeline中传播(ChannelPipeline在下篇文章中会详细介绍)。
注册流程到此基本结束,回到doBind方法中,关注接下来的doBind0()方法:
调用AbstractChannel#bind()的方法:
bind是一个出站时间,在ChannelPipeline中,最终调用head的bind方法:
此处的unsafe是一个NioMessageUnsafe对象,调用unsafe的bind方法如下:
doBind方法调用JDK底层API进行ServerSocket绑定指定端口号的操作。然后出发ChannelPipeline中的ChannelActive事件开始传播,最终会调用AbstractNioUnsafe#beginRead()方法:
获取channel注册时设置的SelectionKey,readInterestOp在上文channel创建的时候,已经将值设置为了SelectionKey.OP_ACCEPT,所以此处是将SelectionKey.OP_ACCEPT设置为ServerSocketChannel注册到Selector中所感兴趣的事件。
NioServerSocketChannel启动注册流程就简要分析完毕。