热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

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

  • 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


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

有一些适配器类可以将编写自定义的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
入站和出站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 之间的交互。


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

至于其(入站下一个,出站上一个)是有一定的编码的如下:
Netty组件(二)—— ChannelHandler、ChannelPipeline和ChannelHandlerContext
如果调用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)方法


推荐阅读
  • 突然觉得服务器ssh密码登录总是浪费一定量的时间,就想试试用sshKey进行登录。生成服务器sshkey和本地sshkey$ssh-keygen在服务器上生成一个authorize ... [详细]
  • 配置OracleACFS集群文件系统
    配置OracleACFS集群文件系统               2012-07-1010:18:39标签:asmacfs版权声明:原创作品,谢绝转载!否则将追究法律责任。     ... [详细]
  • 导读:今天编程笔记来给各位分享关于php动态扩展怎么加载的相关内容,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: ... [详细]
  • rtemsapi用户指南Elixir代表了相对较新的编程语言,面向更广泛的受众。它于2011年发布,此后一直在开发中。他的主要特征是取消功能范式 ... [详细]
  • 系统osx10.11用的是brew下的php56brew下的nginx下了一个项目,在安装过程中提示缺少,intl和apc扩展,就用下面的语句下载了,也装上了,但php还是没有加载 ... [详细]
  • Spring @Primary和@Qualifier注解原理解析
    这篇文章主要介绍了Spring@Primary和@Qualifier注解原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值, ... [详细]
  • C#按值复制数组我有一个类型化的数组MyType[]types;我想制作这个数组的独立副本。我试过这个MyType[]types2newMyType[types.Length];t ... [详细]
  • 开发笔记:Xunit测试使用个人小结
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Xunit测试使用个人小结相关的知识,希望对你有一定的参考价值。因工作中用到xunit测试,故总结下用法,以供个人参考使 ... [详细]
  • DDD在微服务架构中落地应用
    1DDDDomainDrivenDesign(领域驱动设计,DDD),不是一种架构,而是一种架构方法论,是一种拆解业务、划分业务、确定业 ... [详细]
  • 1、对于List而言,要不然就使用迭代器,要不然就从后往前删除,从前往后删除会出现角标越界。因为我List有两个remove方法,一个是int作为形参(删除指定位置的元素),一个是 ... [详细]
  • 自动化部署服务——AWS CodeDeploy 快速入门
    https:amazonaws-china.comcnblogschinagetting-started-with-codedeploy作为DevOps和微服务的深入践行者 ... [详细]
  • PHP 扩展编译的通用方法
    2019独角兽企业重金招聘Python工程师标准以memcache扩展为例子首先需要到软件的官方(如memcached的地址http:pecl.php.netp ... [详细]
  • 《Java并发编程》自旋锁与互斥锁两者非常类似,只是调度策略的不同。对于独占资源的访问,互斥锁在获得锁之前将一直处于休眠状态,自旋锁则是不 ... [详细]
  • 《ASP.NET MVC 4 实战》 1.3  ASP.NET MVC 3/4的新特性
    本节书摘来自异步社区《ASP.NETMVC4实战》一书中的第1章,第1.3节,作者:【美】JeffreyPalermo,【美】JimmyB ... [详细]
  • SpringMVC启动流程——DispatcherServlet由于DispatcherServlet本身就是一个Servlet,它的本质上是一个Servlet,只是子类不断的对H ... [详细]
author-avatar
PLDLYY
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有