程使用Selector监听多路IO的Accept事件,然后将这些套接字交给上面的WorkerGroup,so这里NioServerSocketChannel注册到bossGroup中
bossGroup并不会自己进行注册,而是使用next方法找到自己的小弟——EventLoop,进行注册
这里会使用到EventExecutorChooser进行选择EventLoop,Netty自带两种策略
选择完EventLoop后,会调用EventLoop的注册方法,最终会使用AbstractUnsafe#register,其中会先判断执行的线程是不是EventLoop线程,如果不是那么会将任务提交到EventLoop中执行,源码如下:
注册的即将当前Channel注册到Selector,并且attachment指定为当前Channel,这样NioEventLoop在进行IO多路复用的的时候,可通过attachment方法拿到当前Channel
注册结束后会使用ChannelPipeline触发channelRegistered事件,关于ChannelPipeline下一篇博客中进行学习。
在这一步,还会触发NioEventLoop线程的启动,进行事件循环,在一个死循环中使用Selector监听这个Channel的IO事件,并处理其他调度任务,异步任务。(如何启动NioEventLoop线程的——Netty源码学习2——NioEventLoop的执行#NioEventLoop的启动)
三丶ChannelPipeline
上面我们说到一个Channel的实例化会触发ChannelPipeline的实例化。ChannelPipeline 和 ChannelHandler 也是我们在平时应用开发的过程中打交道最多的组件,通常程序员使用Netty进行开发只需要将自己定义的ChannelHandler加入到ChannelPipeline中。
ChannelPipeline即是ChannelHandler的流水线,ChannelPipeline 可以看作是 ChannelHandler 的容器载体,它是由一组 ChannelHandler 实例组成的,内部通过双向链表将不同的 ChannelHandler 链接在一起,如下图所示。当有 I/O 读写事件触发时,ChannelPipeline 会依次调用 ChannelHandler 列表对 Channel 的数据进行拦截和处理。
1.ChannelHandlerContext
ChannelHandlerContext 用于保存 ChannelHandler 上下文,其包含了 ChannelHandler 生命周期的所有事件,如 connect、bind、read、flush、write、close 等。
2.ChannelInboundHandler 和 ChannelOutboundHandler
在客户端与服务端通信的过程中,数据从客户端发向服务端的过程叫出站,反之称为入站。数据先由一系列 InboundHandler 处理后入站,然后再由相反方向的 OutboundHandler 处理完成后出站。
3.DefaultChannelPipeline
DefaultChannelPipeline是netty中ChannelPipeline的默认实现,内部保存了HeadContext,和TailContext分别作为链表的头和尾
可以看到HeadContext即是ChannelOutboundInvoker(出站处理器)也是ChannelInboundInvoker(出站处理器),这是因为网络数据写入操作的入口就是由 HeadContext 节点完成的。HeadContext 作为 Pipeline 的头结点负责读取数据并开始传递 入站事件,当数据处理完成后,数据会反方向经过各个 ChannelOutboundInvoker的处理,最终传递到 HeadContext。
而TailContext只实现了ChannelInboundInvoker,它是最后一个ChannelInboundInvoker,用于结束入站事件的传播。
4.ChannelInboundHandler&ChannelOutboundHandler
二者都是ChannelHandler的子接口,其方法的声明对于了Netty中对事件的抽象
4.1 ChannelInboundHandler
方法名&事件 |
|
channelRegistered |
当Channel注册到它的EventLoop并且能够处理I/O时调用 |
channelUnregistered |
当Channel从它的EventLoop中注销并且无法处理任何I/O时调用 |
channelActive |
当Channel处理于活动状态时被调用 |
channelInactive |
不再是活动状态且不再连接它的远程节点时被调用 |
channelReadComplete |
当Channel上的一个读操作完成时被调 |
channelRead |
当从Channel读取数据时被调用 |
channelWritabilityChanged |
当Channel的可写状态发生改变时被调用 |
userEventTriggered |
当ChannelInboundHandler.fireUserEventTriggered()方法被调用时触发 |
4.2 ChannelOutBoundHandler
方法名&事件 |
|
bind |
当请求将Channel绑定到本地地址时被调用 |
connet |
当请求将Channel连接到远程节点时被调用 |
disconnect |
当请求将Channel从远程节点断开时调用 |
close |
当请求关闭Channel时调用 |
deregister |
当请求将Channel从它的EventLoop注销时调用 |
read |
当请求从Channel中读取数据时调用 |
flush |
当请求通过Channel将入队数据冲刷到远程节点时调用 |
write |
当请求通过Channel将数据写入远程节点时被调用 |
四丶总结
此篇初探了Channel,ChannelPipeline,ChannelContext,ChannelHandler之间的关系,深入学习了Netty中的Nio Channel是怎么和jdk中的Channel组织起来的。
上面我们说到在Netty中,NioEventLoop是事件的调度中心,它控制了Io事件和其他任务的调度,但是io事件的处理是依赖ChannelHandler的,多个ChannelHandler又由ChannelPipline组装成流水线依次执行
那么一个网络请求在Netty中是