From ca934916229f1b8cbbe9438b8aa642ef6adb0554 Mon Sep 17 00:00:00 2001 From: AmyliaY <471816751@qq.com> Date: Sun, 30 Aug 2020 21:28:51 +0800 Subject: [PATCH] =?UTF-8?q?Netty=E7=9A=84ChannelPipeline=E5=92=8CChannelHa?= =?UTF-8?q?ndler=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../ChannelPipeline和ChannelHandler组件.md | 244 +++++++++++++++++- images/Netty/ChannelHandler组件.png | Bin 0 -> 21883 bytes 3 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 images/Netty/ChannelHandler组件.png diff --git a/README.md b/README.md index b7c62bb..7396c18 100644 --- a/README.md +++ b/README.md @@ -153,8 +153,8 @@ ### Netty 主要组件的源码分析 * [ByteBuf组件](docs/Netty/Netty主要组件源码分析/ByteBuf组件.md) * [Channel组件 和 Unsafe组件](docs/Netty/Netty主要组件源码分析/Channel和Unsafe组件.md) -* [ChannelPipeline 和 ChannelHandler组件](docs/Netty/Netty主要组件源码分析/ChannelPipeline和ChannelHandler组件.md) * [EventLoop 和 EventLoopGroup组件](docs/Netty/Netty主要组件源码分析/EventLoop和EventLoopGroup组件.md) +* [ChannelPipeline 和 ChannelHandler组件](docs/Netty/Netty主要组件源码分析/ChannelPipeline和ChannelHandler组件.md) * [Future 和 Promise组件](docs/Netty/Netty主要组件源码分析/Future和Promise组件.md) ### Netty 高级特性 diff --git a/docs/Netty/Netty主要组件源码分析/ChannelPipeline和ChannelHandler组件.md b/docs/Netty/Netty主要组件源码分析/ChannelPipeline和ChannelHandler组件.md index fcb5dbe..9165f52 100644 --- a/docs/Netty/Netty主要组件源码分析/ChannelPipeline和ChannelHandler组件.md +++ b/docs/Netty/Netty主要组件源码分析/ChannelPipeline和ChannelHandler组件.md @@ -1 +1,243 @@ -努力编写中... \ No newline at end of file +Netty 的 ChannelPipeline 和 ChannelHandler 机制类似于 Servlet 和 Filter过滤器,这类拦截器实际上是职责链模式的一种变形,主要是为了方便事件的拦截和用户业务逻辑的定制。 + +Servlet Filter 能够以声明的方式(web.xml 配置文件)插入到 HTTP请求响应的处理过程中,用于拦截请求和响应,以便能够查看、提取或以某种方式操作正在客户端和服务器之间交换的数据。拦截器封装了业务定制逻辑,能够实现对 Web应用程序 的预处理和事后处理。 + +Netty 的 Channel过滤器 实现原理与 Servlet Filter机制 一致,它将 Channel的数据管道 抽象为 ChannelPipeline,消息在 ChannelPipeline 中流动和传递。ChannelPipeline 持有 I/O事件拦截器 ChannelHandler链表,由 ChannelHandler链表 对 IO事件 进行拦截和处理,可以通过新增和删除 ChannelHandler 来实现不同的业务逻辑定制,不需要对已有的 ChannelHandler 进行修改,能够实现对修改封闭和对扩展的支持。 + +下面我们对 ChannelPipeline 和 ChannelHandler 的功能进行简单地介绍,然后分析下其源码设计。 + +## ChannelPipeline 的功能和作用 +ChannelPipeline 是 ChannelHandler 的容器,它负责 ChannelHandler 的管理、事件拦截与调度。 + +#### ChannelPipeline 的事件处理 +下图展示了 一个消息被 ChannelPipeline 的 ChannelHandler链 拦截和处理的全过程。 + +```java + * I/O Request + * via {@link Channel} or + * {@link ChannelHandlerContext} + * | + * +---------------------------------------------------+---------------+ + * | ChannelPipeline | | + * | \|/ | + * | +---------------------+ +-----------+----------+ | + * | | Inbound Handler N | | Outbound Handler 1 | | + * | +----------+----------+ +-----------+----------+ | + * | /|\ | | + * | | \|/ | + * | +----------+----------+ +-----------+----------+ | + * | | Inbound Handler N-1 | | Outbound Handler 2 | | + * | +----------+----------+ +-----------+----------+ | + * | /|\ . | + * | . . | + * | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()| + * | [ method call] [method call] | + * | . . | + * | . \|/ | + * | +----------+----------+ +-----------+----------+ | + * | | Inbound Handler 2 | | Outbound Handler M-1 | | + * | +----------+----------+ +-----------+----------+ | + * | /|\ | | + * | | \|/ | + * | +----------+----------+ +-----------+----------+ | + * | | Inbound Handler 1 | | Outbound Handler M | | + * | +----------+----------+ +-----------+----------+ | + * | /|\ | | + * +---------------+-----------------------------------+---------------+ + * | \|/ + * +---------------+-----------------------------------+---------------+ + * | | | | + * | [ Socket.read() ] [ Socket.write() ] | + * | | + * | Netty Internal I/O Threads (Transport Implementation) | + * +-------------------------------------------------------------------+ +``` +从上图可以看出 消息读取和发送处理全流程为: +1. 底层的 SocketChannel.read()方法 读取 ByteBuf,触发 ChannelRead事件,由 IO线程 NioEventLoop 调用 ChannelPipeline 的 fireChannelRead(Object msg)方法,将消息传输到 ChannelPipeline 中。 +2. 消息依次被 HeadHandler、ChannelHandler1、ChannelHandler2 … TailHandler 拦截和处理,在这个过程中,任何 ChannelHandler 都可以中断当前的流程,结束消息的传递。 +3. 调用 ChannelHandlerContext 的 write方法 发送消息,消息从 TailHandler 开始途经 ChannelHandlerN … ChannelHandler1、HeadHandler,最终被添加到消息发送缓冲区中等待刷新和发送,在此过程中也可以中断消息的传递,例如当编码失败时,就需要中断流程,构造异常的Future返回。 + +Netty 中的事件分为 Inbound事件 和 Outbound事件。Inbound事件 通常由 I/O线程 触发,例如 TCP链路建立事件、链路关闭事件、读事件、异常通知事件等,它对应上图的左半部分。触发 Inbound事件 的方法如下。 +1. ChannelHandlerContext.fireChannelRegistered():Channel注册事件; +2. ChannelHandlerContext.fireChannelActive():TCP链路建立成功,Channel激活事件; +3. ChannelHandlerContext.fireChannelRead(Object):读事件; +4. ChannelHandlerContext.fireChannelReadComplete():读操作完成通知事件; +5. ChannelHandlerContext.fireExceptionCaught(Throwable):异常通知事件; +6. ChannelHandlerContext.fireUserEventTriggered(Object):用户自定义事件; +7. ChannelHandlerContext.fireChannelWritabilityChanged():Channel的可写状态变化; +8. ChannelHandlerContext.fireChannellnactive():TCP连接关闭,链路不可用通知事件。 + +Outbound事件 通常是由用户主动发起的 网络IO操作,例如用户发起的连接操作、绑定操作、消息发送等操作,它对应上图的右半部分。触发 Outbound事件 的方法如下: +1. ChannelHandlerContext.bind(SocketAddress, ChannelPromise):绑定本地地址事件; +2. ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise):连接服务端事件; +3. ChannelHandlerContext.write(Object, ChannelPromise):发送事件; +4. ChannelHandlerContext.flush():刷新事件; +5. ChannelHandlerContext.read():读事件; +6. ChannelHandlerContext.disconnect(ChannelPromise):断开连接事件; +7. ChannelHandlerContext.close(ChannelPromise):关闭当前Channel事件。 + +#### ChannelPipeline 自定义拦截器 +ChannelPipeline 通过 ChannelHandler 来实现事件的拦截和处理,由于 ChannelHandler 中的事件种类繁多,不同的 ChannelHandler 可能只需要关心其中的个别事件,所以,自定义的ChannelHandler 只需要继承 ChannelInboundHandlerAdapter / ChannelOutboundHandlerAdapter,覆盖自己关心的方法即可。 + +下面的两个示例分别展示了:拦截 Channel Active事件,打印TCP链路建立成功日志,和 链路关闭的时候释放资源,代码如下。 +```java +public class MyInboundHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelActive(ChannelHandlerContext context) { + System.out.println("欢迎来到,LPL!"); + context.fireChannelActive(); + } +} + +public class MyOutboundHandler extends ChannelOutboundHandlerAdapter { + @Override + public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + System.out.println("游戏结束..."); + ctx.close(); + } +} +``` + +#### 构建 pipeline +使用 Netty 时,用户不需要自己创建 pipeline,因为使用 ServerBootstrap 或者 Bootstrap 进行配置后,Netty 会为每个 Channel连接 创建一个独立的pipeline。我们只需将自定义的 ChannelHandler 加入到 pipeline 即可。相关代码如下。 +```java +ServerBootstrap server = new ServerBootstrap(); +server.childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + /** 解析自定义协议 */ + pipeline.addLast(new MyDecoder()); + pipeline.addLast(new MyEncoder()); + pipeline.addLast(new SocketHandler()); + /** 解析Http请求 */ + pipeline.addLast(new HttpServerCodec()); + //主要是将同一个http请求或响应的多个消息对象变成一个 fullHttpRequest完整的消息对象 + pipeline.addLast(new HttpObjectAggregator(64 * 1024)); + //主要用于处理大数据流,比如一个1G大小的文件如果你直接传输肯定会撑暴jvm内存的 ,加上这个handler我们就不用考虑这个问题了 + pipeline.addLast(new ChunkedWriteHandler()); + } +}); +``` +对于类似编解码这样的 ChannelHandler,它存在先后顺序,例如 MessageToMessageDecoder,在它之前往往需要有 ByteToMessageDecoder 将 ByteBuf 解码为对象,然后将对象做二次解码 得到最终的 POJO对象。pipeline 支持指定位置添加或者删除ChannelHandler。 + +#### ChannelPipeline 的主要特性 +ChannelPipeline 支持运行时动态的添加或者删除 ChannelHandler,在某些场景下这个特性非常实用。例如当业务高峰期需要对系统做拥塞保护时,就可以根据当前的系统时间进行判断,如果处于业务高峰期,则动态地将 系统拥塞保护ChannelHandler 添加到当前的ChannelPipeline 中,当高峰期过去之后,再动态删除 拥塞保护ChannelHandler。 + +ChannelPipeline 是线程安全的,这意味着 N 个业务线程可以并发地操作 ChannelPipeline 而不存在多线程并发问题。但 ChannelHandler 不是线程安全的,这意味着 我们需要自己保证 ChannelHandler 的线程安全。 + +## ChannelPipeline 源码解析 +ChannelPipeline 的代码比较简单,它实际上是一个 ChannelHandler容器,内部维护了一个 ChannelHandler 的链表和迭代器,可以方便地进行 ChannelHandler 的 CRUD。 + +另外一个比较重要的部分是,当发生某个 I/O事件 时,如 链路建立、链路关闭、读写操作 等,都会产一个事件,事件在 pipeline 中传播和处理,它是事件处理的总入口。由于 网络I/O 相关的事件有限,因此 Netty 对这些事件进行了统一抽象,Netty 提供的 和用户自定义的 ChannelHandler 会对感兴趣的事件进行拦截和处理。 + +pipeline 中以 fireXXX 命名的方法都是从 I/O线程 流向 用户业务Handler 的 inbound事件,它们的实现因功能而异,但是处理步骤类似,都是 调用HeadHandler 对应的 fireXXX方法,然后执行事件相关的逻辑操作。 + +```java +public interface ChannelPipeline + extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable> { + + /** + * 管理 ChannelHandler 的api + */ + ChannelPipeline addFirst(String name, ChannelHandler handler); + + ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler); + + ChannelPipeline addLast(String name, ChannelHandler handler); + + ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler); + + ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler); + + ChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, ChannelHandler handler); + + ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler); + + ChannelPipeline addAfter(EventExecutorGroup group, String baseName, String name, ChannelHandler handler); + + ChannelPipeline addFirst(ChannelHandler... handlers); + + ChannelPipeline addFirst(EventExecutorGroup group, ChannelHandler... handlers); + + ChannelPipeline addLast(ChannelHandler... handlers); + + ChannelPipeline addLast(EventExecutorGroup group, ChannelHandler... handlers); + + ChannelPipeline remove(ChannelHandler handler); + + ChannelHandler remove(String name); + + T remove(Class handlerType); + + ChannelHandler removeFirst(); + + ChannelHandler removeLast(); + + ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler); + + ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler); + + T replace(Class oldHandlerType, String newName, ChannelHandler newHandler); + + ChannelHandler first(); + + ChannelHandler last(); + + ChannelHandler get(String name); + + T get(Class handlerType); + + /** + * 处理 I/O事件 的api + */ + @Override + ChannelPipeline fireChannelRegistered(); + + @Override + ChannelPipeline fireChannelUnregistered(); + + @Override + ChannelPipeline fireChannelActive(); + + @Override + ChannelPipeline fireChannelInactive(); + + @Override + ChannelPipeline fireExceptionCaught(Throwable cause); + + @Override + ChannelPipeline fireUserEventTriggered(Object event); + + @Override + ChannelPipeline fireChannelRead(Object msg); + + @Override + ChannelPipeline fireChannelReadComplete(); + + @Override + ChannelPipeline fireChannelWritabilityChanged(); + + @Override + ChannelPipeline flush(); +} +``` + +## ChannelHandler 的功能和作用 +ChannelHandler 负责对 I/O事件 进行拦截处理,它可以选择性地 拦截处理感兴趣的事件,也可以透传和终止事件的传递。基于 ChannelHandler接口,我们可以方便地进行业务逻辑定制,如 打印日志、统一封装异常信息、性能统计和消息编解码等。 + +#### ChannelHandlerAdapter +大部分 ChannelHandler 都会选择性 拦截处理感兴趣的 I/O事件,忽略其他事件,然后交由下一个 ChannelHandler 进行拦截处理。这会导致一个问题:自定义 ChannelHandler 必须要实现 ChannelHandler 的所有接口,包括它不关心的那些事件处理接口,这会导致用户代码的冗余和臃肿,代码的可维护性也会变差。 + +为了解决这个问题,Netty 提供了 ChannelHandlerAdapter 基类,和 ChannelInboundHandlerAdapter / ChannelOutboundHandlerAdapter 两个实现类,如果 自定义ChannelHandler 关心某个事件,只需要继承 ChannelInboundHandlerAdapter / ChannelOutboundHandlerAdapter 覆盖对应的方法即可,对于不关心的,可以直接继承使用父类的方法,这样子类的代码就会非常简洁清晰。 + +## ChannelHandler组件 的类结构 +相对于 ByteBuf 和 Channel,ChannelHandler 的类继承关系稍微简单些,但是它的子类非常多,功能各异,主要可以分为如下四类。 +1. ChannelPipeline 的系统 ChannelHandler,用于 I/O操作 和对事件进行预处理,对用户不可见,这类 ChannelHandler 主要包括 HeadHandler 和 TailHandler; +2. 编解码ChannelHandler,如 MessageToMessageEncoder、MessageToMessageDecoder、MessageToMessageCodec; +3. 其他系统功能性 ChannelHandler,如 流量整型Handler、读写超时Handler、日志Handler等; +4. 自定义 ChannelHandler。 + +ChannelHandler组件 的核心类及常用类的类图如下。 + +![在这里插入图片描述](../../../images/Netty/ChannelHandler组件.png) \ No newline at end of file diff --git a/images/Netty/ChannelHandler组件.png b/images/Netty/ChannelHandler组件.png new file mode 100644 index 0000000000000000000000000000000000000000..f5c5d17051b8d7021967da8c69facd9718c79954 GIT binary patch literal 21883 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 literal 0 HcmV?d00001