ToB企服应用市场:ToB评测及商务社交产业平台

标题: Netty源码学习3——Channel ,ChannelHandler,ChannelPipeline [打印本页]

作者: 愛在花開的季節    时间: 2023-9-1 20:41
标题: Netty源码学习3——Channel ,ChannelHandler,ChannelPipeline
系列文章目录和关于我
零丶引入

Netty源码学习2——NioEventLoop的执行中,我们学习了NioEventLoop是如何进行事件循环以及如何修复NIO 空轮询的bug的,但是没有深入了解IO事件在netty中是如何被处理的,下面我们以服务端demo代码为例子,看下和IO事件处理密切的Channel

如上在编写netty 服务端的时候,我们一般只需要指定Channel类型,以及实现ChannelHandler在对应方法中编写业务逻辑代码即可。
在Netty中,NioEventLoop是事件的调度中心,它控制了Io事件和其他任务的调度,但是io事件的处理是依赖ChannelHandler的,多个ChannelHandler又由ChannelPipline组装成流水线依次执行
这篇博客我们以此为切入点,看看Channel是如何初始化的,如何和EventLoop关联起来的,后续看看ChannelPipline是如何组织ChannelHandler的。
一丶Channel概述

Channel是Netty抽象出来的对网络I/O进行读写的相关接口,与JDK NIO中的Channel接口类似。Channel的主要功能有网络I/O的读写绑定端口客户端发起连接主动关闭连接获取通信双方网络地址等。
下面是NioServerSocketChannel,和NioSocketChannel的类图

二丶服务端启动是NioServerSocketChannel是如何实例化并注册到EventLoop中的
  1. 客户端的NioSocketChannel实例化和注册流程与服务端类似
复制代码
如上代码中,服务端指定了Channel类型,然后调用bind方法,在bind方法中会进行Channel的初始化+注册到EventLoop中
1.NioServerSocketChannel的实例化

在不指定ChannelFactory的时候,这里默认是ReflectiveChannelFactory,如同其名称一样,ReflectiveChannelFactory会使用反射的方式构建出Channel

随后会使用SelectorProvider#openServerSocketChannel创建出一个jdk原生的ServerSocketChannel。
然后调用父类构造器,设置Channel为非阻塞,并调用newUnsafe和newChannelPipeline实例化unsafe和channelPipeline

对于服务端来说这里newUnsafe产生的是NioMessageUnsafe,ChannelPipeline通常使用的是DefaultChannelPipeline
2.NioServerSocketChannel的初始化


初始化会将我们在ServerBootStrap中设置的参数设置到NioServerSocketChannel中
并向ChannelPipeline添加一个ServerBootstrapAcceptor,ServerBootstrapAcceptor和Netty的reactor模式有关,此类的作用后续进行学习。
3.NioServerSocketChannel的注册

随后会使用ServerBootStrap中的EventLoopGroup#register方法进行注册,这里的使用的EventLoopGroup是demo中指定的bossGroup
因为在Reactor模式中,bossGroup负责处理ACCEPT事件,单线程使用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中是怎么从NioEventLoop事件循环中交由ChannelPipline进行事件传播与处理的昵?这个下篇中进行学习和总结。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4