资讯专栏INFORMATION COLUMN

Netty 框架总结「ChannelHandler 及 EventLoop」

VioletJack / 1741人阅读

摘要:随着状态发生变化,相应的产生。这些被转发到中的来采取相应的操作。当收到数据或相关的状态改变时,这些方法被调用,这些方法和的生命周期密切相关。主要由一系列组成的。采用的线程模型,在同一个线程的中处理所有发生的事。

「博客搬家」  原地址: 简书  原发表时间: 2017-05-05

学习了一段时间的 Netty,将重点与学习心得总结如下,本文主要总结ChannelHandler 及 EventLoop 的知识点和基本用法,本文章节排序参照《Netty in Action》的章节排序。

以下内容主要参考「并发编程网」的 《Netty in Action》中文版 以及《Netty in Action》原版图书,辅助参考 Essential Netty in Action 《Netty 实战(精髓)》 以及 Netty 官网的 Netty 4.1 JavaDoc 。
6. ChannelHandler 和 ChannelPipeline

一个 Channel 正常的生命周期如下图所示。随着状态发生变化,相应的 event 产生。这些 event 被转发到 ChannelPipeline 中的 ChannelHandler 来采取相应的操作。

6.1 ChannelHandler

ChannelHandler 有两个重要的子接口:

「ChannelInboundHandler」处理输入数据和所有类型的状态变化

「ChannelOutboundHandler」处理输出数据,可以拦截所有操作

6.1.1 ChannelInboundHandler

下表列出接口 ChannelInboundHandler 的方法。当收到数据或相关 Channel 的状态改变时,这些方法被调用,这些方法和Channel的生命周期密切相关

方法 描述
channelRegistered 当一个Channel注册到EventLoop上,可以处理I/O时被调用
channelUnregistered 当一个Channel从它的EventLoop上解除注册,不再处理I/O时被调用
channelActive 当Channel变成活跃状态时被调用;Channel是连接/绑定、就绪的
channelInactive 当Channel离开活跃状态,不再连接到某个远端时被调用
channelReadComplete 当Channel上的某个读操作完成时被调用
channelRead 当从Channel中读数据时被调用
6.1.2 ChannelOutboundHandler

输出的操作和数据由 ChannelOutBoundHandler 处理。它的方法可以被 Channel,ChannelPipeline 和 ChannelHandlerContext 调用,子接口 ChannelOutboundHandler 的主要方法如下:

方法 描述
bind(ChannelHandlerContext,SocketAddress,ChannelPromise) 请求绑定 Channel 到一个本地地址
connect(ChannelHandlerContext, SocketAddress,SocketAddress,ChannelPromise) 请求连接 Channel 到远端
disconnect(ChannelHandlerContext, ChannelPromise) 请求从远端断开 Channel
close(ChannelHandlerContext,ChannelPromise) 请求关闭 Channel
deregister(ChannelHandlerContext, ChannelPromise) 请求 Channel 从它的 EventLoop 上解除注册
read(ChannelHandlerContext) 请求从 Channel 中读更多的数据
flush(ChannelHandlerContext) 请求通过 Channel 刷队列数据到远端
write(ChannelHandlerContext,Object, ChannelPromise) 请求通过 Channel 写数据到远端
6.1.3 ChannelHandler 适配器类

ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 这两个适配器类分别提供了 ChannelInboundHandler 和 ChannelOutboundHandler 的基本实现,它们继承了共同的父接口 ChannelHandler 的方法,扩展了抽象类 ChannelHandlerAdapter。

ChannelHandlerAdapter 提供了工具方法 isSharable()。如果类实现带 @Sharable 注解,那么这个方法就会返回 true,意味着这个对象可以被添加到多个 ChannelPipeline 中。

ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 中的方法调用相关 ChannelHandlerContext 中的等效方法,因此将事件转发到管道中的下一个ChannelHandler。

6.1.4 ChannelFuture 和 ChannelPromise

ChannelPromise 是 ChannelFuture 的子接口

而 ChannelFuture 是不可变对象

ChannelPromise 定义了可写的方法,比如 setSuccess(), setFailure()

6.1.5 释放资源

1. 输入方向「Inbound」
当一个 ChannelInboundHandler 实现类重写 channelRead() 方法时,它要负责释放 ByteBuf 相关的内存。可使用 Netty 提供的工具方法:

    ReferenceCountUtil.release(「ByteBuf 的对象」)

更简单的,可使用子类 SimpleChannelInboundHandler ,一条消息在被 ChannelRead0() 读取后,会被自动释放资源,此时任何对消息的引用都会变成无效,所以不能保存这些引用待后来使用。

2. 输出方向「Outbound」
在输出方向,如果处理一个 write() 操作并且丢弃一条消息(没有写入 Channel),就应该负责释放这条消息。

@ChannelHandler.Sharable public 
class DiscardOutboundHandler extends ChannelOutboundHandlerAdapter {

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
    ReferenceCountUtil.release(msg);  //使用 ReferenceCountUtil.release(...) 释放资源
    promise.setSuccess();  //通知 ChannelPromise 数据已经被处理
}

如果一个消息被“消费”或者丢弃,没有送到 ChannelPipeline 中的下一个 ChannelOutboundHandler,用户就要负责调用 ReferenceCountUtil.release()。如果消息到达了真正的传输层,在它被写到 Socket 中或者 Channel 关闭时,会被自动释放,用户不用管。

6.2 ChannelPipeline 接口

每个新创建的 Channel 都会分配一个新的 ChannelPipeline,Channel 不可以更换或解除当前的 ChannelPipeline,在 Netty 组件的整个生命周期中这个关系是固定的。

一个 ChannelPipeline 可看成是一串 ChannelHandler 实例,拦截穿过 Channel 的输入输出 event。

根据来源,一个 event 可以被一个 ChannelInboundHandler 或 ChannelOutboundHandler 处理。接下来,通过调用 ChannelHandlerContext 的方法,event 会被转发到下一个同类型的 handler。

6.2.1 ChannelHandlerContext

通过 ChannelHandlerContext,一个 handler 可以通知 ChannelPipeline 中的下一个ChannelHandler,甚至动态改动下一个ChannelHandler 所属的 ChannelPipeline。

ChannelPipeline 主要由一系列 ChannelHandler 组成的。ChannelPipeline 提供在 ChannelPipeline 中传送 event 的方法。

ChannelHandlerContext 的一些方法和其他类(Channel 和 ChannelPipeline)的方法名字相似,但是 ChannelHandlerContext 的方法采用了更短的 event 传递路程。我们应该尽可能利用这一点来实现最好的性能。

如果你在 Channel 或者 ChannelPipeline 实例上调用这些方法,它们的调用会穿过整个 pipeline。而在 ChannelHandlerContext 上调用的同样的方法,仅仅从当前 ChannelHandler 开始,走到 pipeline 中下一个可以处理这个 event 的 ChannelHandler。

「本节参考」 第六章 ChannelHandler 和 ChannelPipeline

7. EventLoop 和 EventLoopGroup 7.1 Java 基本的线程池模式

从池中空闲的线程中选出一个,分配一个提交的task「一个Runnable的实现」

当task完成,线程返回池中,等待复用「下一次task分配」

7.2 EventLoop「事件循环」

EventLoop 始终由一个线程驱动

一个 EventLoop 可以被指派来服务多个 Channel

一个 Channel 只拥有一个 EventLoop

task (Runnable或Callable) 可以直接提交到 EventLoop 实现即刻或者延后执行。根据配置和可用的CPU核,可以创建多个 EventLoop 来优化资源利用。

一个 event 的本质决定了它将如何被处理;它可能从网络协议栈传送数据到你的应用,或者反过来,或者做一些完全不一样的事情。但是 event 处理逻辑必须足够通用和灵活,来对付所有可能的情况。

所以,在 Netty 4,所有的 I/O 操作和 event 都是由分配给 EventLoop 的那一个 Thread 来处理的。Netty 4 采用的线程模型,在同一个线程的 EventLoop 中处理所有发生的事。

7.3 EventLoopGroup

EventLoopGroup 负责分配 EventLoop 到新创建的 Channel

异步实现只用了很少 EventLoop,这几个 EventLoop 被所有 Channel 共享

一但 Channel 被指派了一个 EventLoop,在它的整个生命周期过程中,都会用这个 EventLoop

为 Channel 的 I/O 和 event 提供服务的 EventLoop 都包含在一个 EventLoopGroup 中。EventLoop 创建和分配的方式根据传输实现的不同而有所不同。

异步实现只用了很少几个 EventLoop(和它们关联的线程),在目前 Netty 的模型中,这几个 EventLoop 被所有 Channel 共享。这让很多 Channel 被最少数量的线程服务,而不是每个 Channel 分配一个线程。

EventLoopGroup 负责分配一个 EventLoop 到每个新创建的 Channel。在目前的实现中,采用循环 (round-robin) 策略可以满足一个平衡的分配,同一个 Eventloop 还可能会被分配到多个 Channel。

「本节参考」 第七章 EventLoop和线程模型

参考链接

《Netty in Action》中文版

Essential Netty in Action 《Netty 实战(精髓)》

Netty 4.1 JavaDoc

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68250.html

相关文章

  • netty实战》阅读笔记(1)——Netty 的概念体系结构

    摘要:它使用了事件通知以确定在一组非阻塞套接字中有哪些已经就绪能够进行相关的操作。目前,可以把看作是传入入站或者传出出站数据的载体。出站事件是未来将会触发的某个动作的操作结果,这些动作包括打开或者关闭到远程节点的连接将数据写到或者冲刷到套接字。 netty的概念 定义 Netty 是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端。我们可以很简单的...

    solocoder 评论0 收藏0
  • Netty组件入门学习

    摘要:可以用来接收入站事件和数据,随后使用应用程序的业务逻辑进行处理。因为用户并不是关心所有的事件,因此提供了抽象类和。抽象类最常见的一个情况,你的应用程序会利用一个来接受解码消息,并对该数据应用业务逻辑。 Channel、EventLoop和ChannelFuture Channel——Socket; EventLoop——控制流、多线程处理、并发 ChannelFuture异步通知 ...

    qpal 评论0 收藏0
  • Netty学习笔记(二)

    摘要:支持很多协议,并且提供用于数据处理的容器。我们已经知道由特定事件触发。可专用于几乎所有的动作,包括将一个对象转为字节或相反,执行过程中抛出的异常处理。提供了一个容器给链并提供了一个用于管理沿着链入站和出站事件的流动。子类通过进行注册。 前两天写了一点netty相关的知识,并写了一个demo,但是对其原理还是没有深入,今天我们来做一次研究吧 首先让我们来认识一下netty的几个核心人物吧...

    0x584a 评论0 收藏0
  • Netty4.x 源码实战系列(四):Pipeline全剖析

    摘要:在上一篇源码实战系列三全剖析中,我们详细分析了的初始化过程,并得出了如下结论在中,每一个都有一个对象,并且其内部本质上就是一个双向链表本篇我们将深入源码内部,对其一探究竟,给大家一个全方位解析。 在上一篇《Netty4.x 源码实战系列(三):NioServerSocketChannel全剖析》中,我们详细分析了NioServerSocketChannel的初始化过程,并得出了如下结论...

    13651657101 评论0 收藏0
  • ChannelPipeline 和 ChannelHandler

    摘要:概念与概念一致用以连接设备文件等的纽带例如将网络的读写客户端发起连接主动关闭连接链路关闭获取通信双方的网络地址等的类型主要有两种非阻塞以及阻塞数据传输类型有两种按事件消息传递以及按字节传递适用方类型也有两种服务器以及客户端还有一些根据传输协 ChannelHandler Channel Channel 概念与 java.nio.channel 概念一致, 用以连接IO设备 (socke...

    William_Sang 评论0 收藏0

发表评论

0条评论

VioletJack

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<