Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext

Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext

  • ChannelHandler
      • ChannelHandler生命周期
      • ChannelInboundHandler接口
      • ChannelOutboundHandler接口
      • ChannelHandler接口
  • ChannelPipeline
      • ChannelPipeline常用方法
  • ChannelHandlerContext
      • ChannelHandlerContext常用方法


ChannelHandler

在日常的开发中,Netty的主要组件是ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器,ChannelHandler 的方法是由网络事件触发的。事实上,ChannelHandler 可专门用于几乎任何类型的动作,例如将数据从一种格式转换为另外一种格式,例如各种编解码,或者处理转换过程中所抛出的异常。所以ChannelInboundHandler是一个我们将会经常实现的子接口。

这种类型的ChannelHandler接收入站事件和数据,这些数据随后将会被你的应用程序的业务逻辑所处理。当你要给连接的客户端发送响应时,也可以从ChannelInboundHandler冲刷数据。你的应用程序的业务逻辑通常驻留在一个或者多个ChannelInboundHandler 中。这种类型的ChannelHandler接收入站事件和数据,这些数据随后将会被你的应用程序的业务逻辑所处理。


ChannelHandler生命周期

下面列出了interface ChannelHandler 定义的生命周期操作,在ChannelHandler被添加到ChannelPipeline 中或者被从ChannelPipeline中移除时会调用这些操作。这些方法中的每一个都接受一个ChannelHandlerContext 参数。

  1. handlerAdded   当把ChannelHandler 添加到ChannelPipeline 中时被调用
  2. handlerRemoved  当从ChannelPipeline 中移除ChannelHandler 时被调用
  3. exceptionCaught  当处理过程中在ChannelPipeline 中有错误产生时被调用

Netty 定义了下面两个重要的ChannelHandler 子接口:
Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext_第1张图片

  • ChannelInboundHandler —— 处理入站数据以及各种状态变化
  • ChannelOutboundHandler —— 处理出站数据并且允许拦截所有的操作

ChannelInboundHandler接口

下面列出了interface ChannelInboundHandler 的生命周期方法。这些方法将会在数据被接收时或者与其对应的Channel 状态发生改变时被调用。正如我们前面所提到的,这些方法和Channel 的生命周期密切相关。

  1. channelRegistered   当Channel 已经注册到它的EventLoop并且能够处理I/时被调用
  2. channelUnregistered  当Channel 从它的EventLoop 注销并且无法处理任何I/O 时被调用
  3. channelActive     当Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪
  4. channelInactive    当Channel 离开活动状态并且不再连接它的远程节点时被调用
  5. channelReadComplete  当Channel上的一个读操作完成时被调用
  6. channelRead      当从Channel 读取数据时被调用
  7. ChannelWritability-Changed  当Channel 的可写状态发生改变时被调用。用户可以确保写操作不会完成得太快(以避免发生OutOfMemoryError)或者可以在Channel变为再次可写时恢复写入。
    可以通过调用Channel 的isWritable()方法来检测Channel的可写性。与可写性相关的阈值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWater-Mark()方法来设置
  8. userEventTriggered  当ChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用。

当某个ChannelInboundHandler 的实现重写channelRead()方法时,它要负责显式地释放与池化的ByteBuf实例相关的内存。Netty 为此提供了一个实用方法ReferenceCountUtil.release()


Netty 将使用WARN 级别的日志消息记录未释放的资源,使得可以非常简单地在代码中发现违规的实例。但是以这种方式管理资源可能很繁琐。一个更加简单的方式是使用SimpleChannelInboundHandler,其会自动释放资源。
Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext_第2张图片


ChannelOutboundHandler接口

出站操作和数据将由ChannelOutboundHandler 处理。它的方法将被Channel、ChannelPipeline 以及ChannelHandlerContext 调用。所有由ChannelOutboundHandler 本身所定义的方法:

  1. bind(ChannelHandlerContext,SocketAddress,ChannelPromise)
    当请求将Channel 绑定到本地地址时被调用

  2. connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise)
    当请求将Channel 连接到远程节点时被调用

  3. disconnect(ChannelHandlerContext,ChannelPromise)
    当请求将Channel 从远程节点断开时被调用

  4. close(ChannelHandlerContext,ChannelPromise)
    当请求关闭Channel 时被调用

  5. deregister(ChannelHandlerContext,ChannelPromise)
    当请求将Channel 从它的EventLoop 注销时被调用

  6. read(ChannelHandlerContext)
    当请求从Channel 读取更多的数据时被调用

  7. flush(ChannelHandlerContext)
    当请求通过Channel 将入队数据冲刷到远程节点时被调用

  8. write(ChannelHandlerContext,Object,ChannelPromise)
    当请求通过Channel 将数据写到远程节点时被调用


ChannelHandler接口

Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext_第3张图片

有一些适配器类可以将编写自定义的ChannelHandler所需要的努力降到最低限度,因为它们提供了定义在对应接口中的所有方法的默认实现。因为你有时会忽略那些不感兴趣的事件,所以Netty提供了抽象基类ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter。

你可以使用ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter类作为自己的ChannelHandler 的起始点。这两个适配器分别提供了ChannelInboundHandler和ChannelOutboundHandler的基本实现。通过扩展抽象类ChannelHandlerAdapter,它们获得了它们共同的超接口ChannelHandler 的方法。

ChannelHandlerAdapter还提供了实用方法isSharable()。如果其对应的实现被标注为Sharable,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline。



ChannelPipeline

当Channel被创建时,它会被自动地分配到它专属的ChannelPipeline。每一个新创建的Channel都将会被分配一个新的ChannelPipeline。这项关联是永久性的,Channel既不能附加另外一个ChannelPipeline,也不能分离其当前的。在Netty 组件的生命周期中,这是一项固定的操作,不需要开发人员的任何干预。

这样使得事件流经ChannelPipeline是ChannelHandler的工作,它们是在应用程序的初始化或者引导阶段被安装的。这些对象接收事件、执行它们所实现的处理逻辑,并将数据传递给链中的下一个ChannelHandler。它们的执行顺序是由它们被添加的顺序所决定的。


Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext_第4张图片
入站和出站ChannelHandler可以被安装到同一个ChannelPipeline中。如果一个消息或者任何其他的入站事件被读取,那么它会从ChannelPipeline的头部开始流动,最终数据将会到达ChannelPipeline 的尾端,这样所有处理就都结束了。

数据的出站运动(即正在被写的数据)在概念上也是一样的。在这种情况下,数据将从ChannelOutboundHandler链的尾端开始流动,直到它到达链的头部为止。在这之后,出站数据将会到达网络传输层,这里显示为Socket。通常情况下,这将触发一个写操作。


如果将两个类别的ChannelHandler都混合添加到同一个ChannelPipeline中会发生什么。虽然ChannelInboundHandle和ChannelOutboundHandle都扩展自ChannelHandler,但是Netty 能区分ChannelInboundHandler实现和ChannelOutboundHandler实现,并确保数据只会在具有相同定向类型的两个ChannelHandler之间传递。


ChannelPipeline常用方法

addFirst()、addBefore()、addAfter()、addLast()
将一个ChannelHandler 添加到ChannelPipeline中

remove()
将一个ChannelHandler 从ChannelPipeline中移除

replace()
将ChannelPipeline 中的一个ChannelHandler替换为另一个ChannelHandler

get()
通过类型或者名称返回ChannelHandler

context()
返回和ChannelHandler绑定的ChannelHandlerContext

names()
返回ChannelPipeline中所有ChannelHandler 的名称ChannelPipeline的API 公开了用于调用入站和出站操作的附加方法。



ChannelHandlerContext

通过使用作为参数传递到每个方法的ChannelHandlerContext,事件可以被传递给当前ChannelHandler 链中的下一个ChannelHandler。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。


ChannelHandlerContext代表了ChannelHandler 和ChannelPipeline之间的关联,每当有ChannelHandler 添加到ChannelPipeline 中时,都会创建ChannelHandlerContext。ChannelHandlerContext 的主要功能是管理它所关联的ChannelHandler 和在同一个ChannelPipeline 中的其他ChannelHandler 之间的交互。

Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext_第5张图片


ChannelHandlerContext 有很多的方法,其中一些方法也存在于Channel 和ChannelPipeline 本身上,但是有一点重要的不同。
Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext_第6张图片
如果调用Channel或者ChannelPipeline上的这些方法,它们将沿着整个ChannelPipeline 进行传播。而调用位于ChannelHandlerContext上的相同方法,则将从当前所关联的ChannelHandler开始,并且只会传播给位于该ChannelPipeline 中的下一个(入站下一个,出站上一个)能够处理该事件的ChannelHandler。

至于其(入站下一个,出站上一个)是有一定的编码的如下:
Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext_第7张图片
如果调用Channel或者ChannelPipeline上的这些方法,可能会从5开始进行传播(如果后来的还有出站处理器6、7、8…,则会从最后一个8…开始向前传播),而直接调用ChannelHandlerContext的方法,则会从3开始,这样传播路径就会较短。


ChannelHandlerContext常用方法

当使用ChannelHandlerContext 的API 的时候,有以下两点:

  • ChannelHandlerContext和ChannelHandler之间的关联(绑定)是永远不会改变的,所以缓存对它的引用是安全的
  • ChannelHandlerContext的方法将产生更短的事件流,应该尽可能地利用这个特性来获得最大的性能

alloc()
返回和这个实例相关联的Channel 所配置的ByteBufAllocator

bind()
绑定到给定的SocketAddress,并返回ChannelFuture

channel()
返回绑定到这个实例的Channel

close()
关闭Channel,并返回ChannelFuture

connect()
连接给定的SocketAddress,并返回ChannelFuture

deregister()
从之前分配的EventExecutor 注销,并返回ChannelFuture

disconnect()
从远程节点断开,并返回ChannelFuture

executor()
返回调度事件的EventExecutor

fireChannelActive()
触发对下一个ChannelInboundHandler上的channelActive()方法(已连接)的调用

fireChannelInactive()
触发对下一个ChannelInboundHandler上的channelInactive()方法(已关闭)的调用

fireChannelRead()
触发对下一个ChannelInboundHandler上的channelRead()方法(已接收的消息)的调用

fireChannelReadComplete()
触发对下一个ChannelInboundHandler上的channelReadComplete()方法的调用

fireChannelRegistered()
触发对下一个ChannelInboundHandler上的fireChannelRegistered()方法的调用

fireChannelUnregistered()
触发对下一个ChannelInboundHandler上的fireChannelUnregistered()方法的调用

fireChannelWritabilityChanged()
触发对下一个ChannelInboundHandler上的fireChannelWritabilityChanged()方法的调用

fireExceptionCaught()
触发对下一个ChannelInboundHandler上的fireExceptionCaught(Throwable)方法的调用

fireUserEventTriggered()
触发对下一个ChannelInboundHandler上的fireUserEventTriggered(Object evt)方法的调用

handler()
返回绑定到这个实例的ChannelHandler

isRemoved()
如果所关联的ChannelHandler已经被从ChannelPipeline中移除则返回true

name()
返回这个实例的唯一名称

pipeline()
返回这个实例所关联的ChannelPipeline

read()
将数据从Channel读取到第一个入站缓冲区;如果读取成功则触发一个channelRead事件,并(在最后一个消息被读取完成后)通知ChannelInboundHandler的channelReadComplete(ChannelHandlerContext)方法

你可能感兴趣的:(Netty)