Netty In Action中文版 - 第六章:ChannelHandler
来源:互联网 发布:mac bootcamp win7 编辑:程序博客网 时间:2024/05/16 04:50
本章介绍
- ChannelPipeline
- ChannelHandlerContext
- ChannelHandler
- Inbound vs outbound(入站和出站)
6.1 ChannelPipeline
ChannelPipeline是ChannelHandler实例的列表,用于处理或截获通道的接收和发送数据。ChannelPipeline提供了一种高级的截取过滤器模式,让用户可以在ChannelPipeline中完全控制一个事件及如何处理ChannelHandler与ChannelPipeline的交互。
对于每个新的通道,会创建一个新的ChannelPipeline并附加至通道。一旦连接,Channel和ChannelPipeline之间的耦合是永久性的。Channel不能附加其他的ChannelPipeline或从ChannelPipeline分离。
下图描述了ChannelHandler在ChannelPipeline中的I/O处理,一个I/O操作可以由一个ChannelInboundHandler或ChannelOutboundHandler进行处理,并通过调用ChannelInboundHandler处理入站IO或通过ChannelOutboundHandler处理出站IO。
如上图所示,ChannelPipeline是ChannelHandler的一个列表;如果一个入站I/O事件被触发,这个事件会从第一个开始依次通过ChannelPipeline中的ChannelHandler;若是一个入站I/O事件,则会从最后一个开始依次通过ChannelPipeline中的ChannelHandler。ChannelHandler可以处理事件并检查类型,如果某个ChannelHandler不能处理则会跳过,并将事件传递到下一个ChannelHandler。ChannelPipeline可以动态添加、删除、替换其中的ChannelHandler,这样的机制可以提高灵活性。
修改ChannelPipeline的方法:
- addFirst(...),添加ChannelHandler在ChannelPipeline的第一个位置
- addBefore(...),在ChannelPipeline中指定的ChannelHandler名称之前添加ChannelHandler
- addAfter(...),在ChannelPipeline中指定的ChannelHandler名称之后添加ChannelHandler
- addLast(ChannelHandler...),在ChannelPipeline的末尾添加ChannelHandler
- remove(...),删除ChannelPipeline中指定的ChannelHandler
- replace(...),替换ChannelPipeline中指定的ChannelHandler
- ChannelPipeline pipeline = ch.pipeline();
- FirstHandler firstHandler = new FirstHandler();
- pipeline.addLast("handler1", firstHandler);
- pipeline.addFirst("handler2", new SecondHandler());
- pipeline.addLast("handler3", new ThirdHandler());
- pipeline.remove("handler3");
- pipeline.remove(firstHandler);
- pipeline.replace("handler2", "handler4", new FourthHandler());
ChannelPipeline除了一些修改的方法,还有很多其他的方法,具体是方法及使用可以看API文档或源码。
6.2 ChannelHandlerContext
每个ChannelHandler被添加到ChannelPipeline后,都会创建一个ChannelHandlerContext并与之创建的ChannelHandler关联绑定。ChannelHandlerContext允许ChannelHandler与其他的ChannelHandler实现进行交互,这是相同ChannelPipeline的一部分。ChannelHandlerContext不会改变添加到其中的ChannelHandler,因此它是安全的。
6.2.1 通知下一个ChannelHandler
在相同的ChannelPipeline中通过调用ChannelInboundHandler和ChannelOutboundHandler中各个方法中的一个方法来通知最近的handler,通知开始的地方取决你如何设置。下图显示了ChannelHandlerContext、ChannelHandler、ChannelPipeline的关系:
如果你想有一些事件流全部通过ChannelPipeline,有两个不同的方法可以做到:
- 调用Channel的方法
- 调用ChannelPipeline的方法
下面的代码显示了一个写事件如何通过ChannelPipeline从尾部开始:
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- //Event via Channel
- Channel channel = ctx.channel();
- channel.write(Unpooled.copiedBuffer("netty in action", CharsetUtil.UTF_8));
- //Event via ChannelPipeline
- ChannelPipeline pipeline = ctx.pipeline();
- pipeline.write(Unpooled.copiedBuffer("netty in action", CharsetUtil.UTF_8));
- }
- });
- }
可能你想从ChannelPipeline的指定位置开始,不想流经整个ChannelPipeline,如下情况:
- 为了节省开销,不感兴趣的ChannelHandler不让通过
- 排除一些ChannelHandler
- // Get reference of ChannelHandlerContext
- ChannelHandlerContext ctx = ..;
- // Write buffer via ChannelHandlerContext
- ctx.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8));
如上图显示的,从指定的ChannelHandlerContext开始,跳过前面所有的ChannelHandler,使用ChannelHandlerContext操作是常见的模式,最常用的是从ChannelHanlder调用操作,也可以在外部使用ChannelHandlerContext,因为这是线程安全的。
6.2.2 修改ChannelPipeline
调用ChannelHandlerContext的pipeline()方法能访问ChannelPipeline,能在运行时动态的增加、删除、替换ChannelPipeline中的ChannelHandler。可以保持ChannelHandlerContext供以后使用,如外部Handler方法触发一个事件,甚至从一个不同的线程。
下面代码显示了保存ChannelHandlerContext供之后使用或其他线程使用:
- public class WriteHandler extends ChannelHandlerAdapter {
- private ChannelHandlerContext ctx;
- @Override
- public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
- this.ctx = ctx;
- }
- public void send(String msg){
- ctx.write(msg);
- }
- }
- @Sharable
- public class NotSharableHandler extends ChannelInboundHandlerAdapter {
- private int count;
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- count++;
- System.out.println("channelRead(...) called the " + count + " time");
- ctx.fireChannelRead(msg);
- }
- }
为什么要共享ChannelHandler?使用@Sharable注解共享一个ChannelHandler在一些需求中还是有很好的作用的,如使用一个ChannelHandler来统计连接数或来处理一些全局数据等等。
6.3 状态模型
Netty有一个简单但强大的状态模型,并完美映射到ChannelInboundHandler的各个方法。下面是Channel生命周期四个不同的状态:
- channelUnregistered
- channelRegistered
- channelActive
- channelInactive
还可以看到额外的状态变化,因为用户允许从EventLoop中注销Channel暂停事件执行,然后再重新注册。在这种情况下,你会看到多个channelRegistered和channelUnregistered状态的变化,而永远只有一个channelActive和channelInactive的状态,因为一个通道在其生命周期内只能连接一次,之后就会被回收;重新连接,则是创建一个新的通道。
下图显示了从EventLoop中注销Channel后再重新注册的状态变化:
6.4 ChannelHandler和其子类
Netty中有3个实现了ChannelHandler接口的类,其中2个是接口,一个是抽象类。如下图:
6.4.1 ChannelHandler中的方法
Netty定义了良好的类型层次结构来表示不同的处理程序类型,所有的类型的父类是ChannelHandler。ChannelHandler提供了在其生命周期内添加或从ChannelPipeline中删除的方法。
- handlerAdded,ChannelHandler添加到实际上下文中准备处理事件
- handlerRemoved,将ChannelHandler从实际上下文中删除,不再处理事件
- exceptionCaught,处理抛出的异常
6.4.2 ChannelInboundHandler
ChannelInboundHandler提供了一些方法再接收数据或Channel状态改变时被调用。下面是ChannelInboundHandler的一些方法:
- channelRegistered,ChannelHandlerContext的Channel被注册到EventLoop;
- channelUnregistered,ChannelHandlerContext的Channel从EventLoop中注销
- channelActive,ChannelHandlerContext的Channel已激活
- channelInactive,ChannelHanderContxt的Channel结束生命周期
- channelRead,从当前Channel的对端读取消息
- channelReadComplete,消息读取完成后执行
- userEventTriggered,一个用户事件被处罚
- channelWritabilityChanged,改变通道的可写状态,可以使用Channel.isWritable()检查
- exceptionCaught,重写父类ChannelHandler的方法,处理异常
看下面代码:
- /**
- * 实现ChannelInboundHandlerAdapter的Handler,不会自动释放接收的消息对象
- * @author c.k
- *
- */
- public class DiscardHandler extends ChannelInboundHandlerAdapter {
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- //手动释放消息
- ReferenceCountUtil.release(msg);
- }
- }
- /**
- * 继承SimpleChannelInboundHandler,会自动释放消息对象
- * @author c.k
- *
- */
- public class SimpleDiscardHandler extends SimpleChannelInboundHandler<Object> {
- @Override
- protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
- //不需要手动释放
- }
- }
ChannelInitializer用来初始化ChannelHandler,将自定义的各种ChannelHandler添加到ChannelPipeline中。
6.4.3 ChannelOutboundHandler
ChannelOutboundHandler用来处理“出站”的数据消息。ChannelOutboundHandler提供了下面一些方法:
- bind,Channel绑定本地地址
- connect,Channel连接操作
- disconnect,Channel断开连接
- close,关闭Channel
- deregister,注销Channel
- read,读取消息,实际是截获ChannelHandlerContext.read()
- write,写操作,实际是通过ChannelPipeline写消息,Channel.flush()属性到实际通道
- flush,刷新消息到通道
看下面的代码:
- public class DiscardOutboundHandler extends ChannelOutboundHandlerAdapter {
- @Override
- public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
- ReferenceCountUtil.release(msg);
- promise.setSuccess();
- }
- }
如果消息被消费并且没有被传递到ChannelPipeline中的下一个ChannelOutboundHandler,那么就需要调用ReferenceCountUtil.release(message)来释放消息资源。一旦消息被传递到实际的通道,它会自动写入消息或在通道关闭是释放。
0 0
- Netty In Action中文版 - 第六章:ChannelHandler
- Netty In Action中文版 - 第六章:ChannelHandler
- 《Netty in Action》中文版—第六章 ChannelHandler和ChannelPipeline
- Netty In Action 读书笔记 - 第六章 ChannelHandler
- netty in action第六章-ChannelHandler
- Netty In Action中文版 - 第八章:附带的ChannelHandler和Codec
- Netty In Action中文版 - 第八章:附带的ChannelHandler和Codec
- netty in action第八章-附带的ChannelHandler和Codec
- Netty in action—ChannelHandler和ChannelPipeline
- Netty in Action (十五) 第六章节 第一部分 ChannelHandler和ChannelPipeline
- Netty In Action中文版
- Netty In Action中文版 - 第十一章:WebSocket
- Netty In Action中文版 - 第十二章:SPDY
- Netty In Action中文版 - 第十一章:WebSocket
- Netty In Action中文版 - 第十二章:SPDY
- Netty In Action中文版 - 第二章:第一个Netty程序
- Netty In Action中文版 - 第三章:Netty核心概念
- Netty In Action中文版 - 第九章:引导Netty应用程序
- Netty In Action中文版 - 第五章:Buffers(缓冲)
- 熊绎:我看软件工程师的职业规划
- Hanoi汉诺塔 问题
- Macbook Pro U盘制作Mavericks安装盘
- WebGrid 详解
- Netty In Action中文版 - 第六章:ChannelHandler
- 【CODE VS】P1695 windows 2013(DP)
- ThreadPoolExecutor的使用和介绍
- Jersey入门教程
- 黑马程序员 java 基础 毕向东 面向对象 多线程 通信 优化 jdk 1.5 之后新特性Lock
- 再谈软件工程
- 我的项目2用Canvas写的一些图形
- Netty In Action中文版 - 第七章:编解码器Codec
- MMU详解