本文主要介绍如下几个相关类:
• ChannelPipeline
• ChannelHandlerContext
• ChannelHandler
• Inbound vs outbound(入站和出站)java
接受客户端的链接和建立链接只是应用程序中的一步,更加剧要的仍是处理传入传出的数据。netty提供了强大的事件处理机制,容许用户自定义ChannelHandler的实现来处理数据。安全
ChannelPipeline是ChannelHandler实例的列表(或则说是容器),用于处理或截获通道的接收和发送数据。ChannelPipeline提供了一种高级的截取过滤器模式,让用户能够在ChannelPipeline中彻底控制一个事件及如何处理ChannelHandler与ChannelPipeline的交互。ide
能够这样说,一个新的通道就对应一个新的ChannelPipeline并附加至通道。一旦链接,通道Channel和ChannelPipeline之间的耦合是永久性的。通道Channel不能附加其余的ChannelPipeline或从ChannelPipeline分离。oop
下图显示了ChannelHandler在ChannelPipeline中的IO处理示意图:
this
很明显,ChannelPipeline里面就是一个ChannelHandler的列表。若是一个入站IO事件被触发,这个事件会从第一个开始依次经过ChannelPipeline中的ChannelHandler。如果一个入站I/O事件,则会从最后一个开始依次经过ChannelPipeline中的ChannelHandler。ChannelHandler能够处理事件并检查类型,若是某个ChannelHandler不能处理则会跳过,并将事件传递到下一个ChannelHandler。ChannelPipeline能够动态添加、删除、替换其中的ChannelHandler,这样的机制能够提升灵活性。spa
修改ChannelPipeline的方法:
• addFirst(…),添加ChannelHandler在ChannelPipeline的第一个位置
• addBefore(…),在ChannelPipeline中指定的ChannelHandler名称以前添加ChannelHandler
• addAfter(…),在ChannelPipeline中指定的ChannelHandler名称以后添加ChannelHandler
• addLast(ChannelHandler…),在ChannelPipeline的末尾添加ChannelHandler
• remove(…),删除ChannelPipeline中指定的ChannelHandler
• replace(…),替换ChannelPipeline中指定的ChannelHandler线程
好比:netty
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的ChannelHandler将经过IO-Thread处理事件,这意味了必须不能有其余的IO-Thread阻塞来影响IO的总体处理;有时候可能须要阻塞,例如JDBC。所以netty容许经过一个EventExecutorGroup到每个ChannelPipeline.add*方法,自定义的事件会被包含在EventExecutorGroup中的EventExecutor来处理,默认的实现是DefaultEventExecutorGroup。code
每一个ChannelHandler被添加到ChannelPipeline后,都会建立一个ChannelHandlerContext并与之建立的ChannelHandler关联绑定。ChannelHandlerContext容许ChannelHandler与其余的ChannelHandler实现进行交互。ChannelHandlerContext不会改变添加到其中的ChannelHandler,所以它是安全的。对象
下图显示了ChannelHandlerContext、ChannelHandler、ChannelPipeline的关系:
若是咱们想有一些事件流所有经过ChannelPipeline,有两个不一样的方法能够作到:
• 调用Channel的方法
• 调用ChannelPipeline的方法
这两个方法均可以让事件流所有经过ChannelPipeline。不管从头部仍是尾部开始,由于它主要依赖于事件的性质。若是是一个“入站”事件,它开始于头部;如果一个“出站”事件,则开始于尾部。
下面的代码显示了一个写事件如何经过ChannelPipeline从尾部开始:
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)); } }); }
下图表示经过Channel或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); } }
请注意,ChannelHandler实例若是带有@Sharable注解则能够被添加到多个ChannelPipeline。也就是说单个ChannelHandler实例能够有多个ChannelHandlerContext,所以能够调用不一样ChannelHandlerContext获取同一个ChannelHandler。若是添加不带@Sharable注解的ChannelHandler实例到多个ChannelPipeline则会抛出异常;使用@Sharable注解后的ChannelHandler必须在不一样的线程和不一样的通道上安全使用。怎么是不安全的使用?看下面代码:
@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); } }
上面是一个带@Sharable注解的Handler,它被多个线程使用时,里面count是不安全的,会致使count值错误。
为何要共享ChannelHandler?使用@Sharable注解共享一个ChannelHandler在一些需求中仍是有很好的做用的,如使用一个ChannelHandler来统计链接数或来处理一些全局数据等等。
Netty有一个简单但强大的状态模型,并完美映射到ChannelInboundHandler的各个方法。下面是Channel生命周期四个不一样的状态:
1. channelUnregistered
2. channelRegistered
3. channelActive
4. channelInactive
Channel的状态在其生命周期中变化,由于状态变化须要触发,下图显示了Channel状态变化:
先看一些类的继承图:
Netty定义了良好的类型层次结构来表示不一样的处理程序类型,全部的类型的父类是ChannelHandler。ChannelHandler提供了在其生命周期内添加或从ChannelPipeline中删除的方法。
1. handlerAdded,ChannelHandler添加到实际上下文中准备处理事件
2. handlerRemoved,将ChannelHandler从实际上下文中删除,再也不处理事件
3. exceptionCaught,处理抛出的异常
netty还提供了一个实现了ChannelHandler的抽象类ChannelHandlerAdapter。ChannelHandlerAdapter实现了父类的全部方法,基本上就是传递事件到ChannelPipeline中的下一个ChannelHandler直到结束。咱们也能够直接继承于ChannelHandlerAdapter,而后重写里面的方法。
ChannelInboundHandler提供了一些方法再接收数据或Channel状态改变时被调用。下面是ChannelInboundHandler的一些方法:
1. channelRegistered,ChannelHandlerContext的Channel被注册到EventLoop;
2. channelUnregistered,ChannelHandlerContext的Channel从EventLoop中注销
3. channelActive,ChannelHandlerContext的Channel已激活
4. channelInactive,ChannelHanderContxt的Channel结束生命周期
5. channelRead,从当前Channel的对端读取消息
6. channelReadComplete,消息读取完成后执行
7. userEventTriggered,一个用户事件被处罚
8. channelWritabilityChanged,改变通道的可写状态,可使用Channel.isWritable()检查
9. exceptionCaught,重写父类ChannelHandler的方法,处理异常
netty提供了一个实现了ChannelInboundHandler接口并继承ChannelHandlerAdapter的类:ChannelInboundHandlerAdapter。ChannelInboundHandlerAdapter实现了ChannelInboundHandler的全部方法,做用就是处理消息并将消息转发到ChannelPipeline中的下一个ChannelHandler。ChannelInboundHandlerAdapter的channelRead方法处理完消息后不会自动释放消息,若想自动释放收到的消息,可使用SimpleChannelInboundHandler。
看下面的代码:
/** * 实现ChannelInboundHandlerAdapter的Handler,不会自动释放接收的消息对象 * @author c.k * */ public class DiscardHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //手动释放消息 ReferenceCountUtil.release(msg); } }
SimpleChannelInboundHandler会自动释放消息
/** * 继承SimpleChannelInboundHandler,会自动释放消息对象 * @author c.k * */ public class SimpleDiscardHandler extends SimpleChannelInboundHandler<Object> { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { //不须要手动释放 } }
ChannelInitializer用来初始化ChannelHandler,将自定义的各类ChannelHandler添加到ChannelPipeline中。