Java Netty 学习(十) - ChannelHandler概览

本篇文章来分析ChannelHandler的相关知识
上一章了解到,ChannelHandler起着一个过滤器作用,用于处理在ChannelPipeline中流动的字节流。
下面慢慢来看。

ChannelInboundHandler

ChannelInboundHandler主要用于处理入站数据以及各种状态变化,它是ChannelHandler的子类:

public interface ChannelInboundHandler extends ChannelHandler {
   // 提供注册Channel注册到EventLoop的事件,提供了ChannelHandler自己的ChannelHandlerContext
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;

  	// Channel取消注册EventLoop的事件
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;

    // 判断Channel是否active
    void channelActive(ChannelHandlerContext ctx) throws Exception;

    // 判断Channel是否unactive
    void channelInactive(ChannelHandlerContext ctx) throws Exception;

    // 当前的Channel从流中读取了一条消息
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;

    // 当前的读操作的最后一个字节已经被channelRead消费
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

    // 当用户事件被触发
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

    // 当Channel的写状态改变时
    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;

    // 出错时
    @Override
    @SuppressWarnings("deprecation")
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

当然Netty还提供了一个包装类ChannelInboundHandlerAdapter,继承这个类,就不用实现ChannelInboundHandler从而实现所有方法了。而ChannelInboundHandlerAdapter给所有方法都提供了默认实现,实质上是调用了ChannelHandlerContext的方法。
在这里插入图片描述

ChannelOutboundHandler

ChannelOutboundHandler则是处理出站数据并且允许拦截所有操作,当用户调用write方法时,就根据out这根链一路传递到Channel再流转到网络上:

public interface ChannelOutboundHandler extends ChannelHandler {
    // 一旦绑定上就调用,而当操作完成时候由promise进行提醒
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

    // 当连接上时候,触发
    void connect(
            ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception;
    // 断开连接是触发
    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    // 当关闭操作时被调用
    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    // 当从当前EventLoop取消注册时调用
    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    // 拦截ChannelHandlerContext的read方法
    void read(ChannelHandlerContext ctx) throws Exception;
    // 当一个write操作被调用时调用
    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
    // flush操作,清空缓存,并把所有缓存数据写入
    void flush(ChannelHandlerContext ctx) throws Exception;
}

当然,ChannelOutboundHandler也提供了一个包装类ChannelOutboundHandlerAdapter这里就不细细分析了。

ByteBuf

Netty中,用ByteBuf作为数据容器,ByteBuf是一个抽象类,里面包括了char,int,long,boolean,float,double,byte,short 等8种java基本类型的抽象set和get,子类可以根据不同需求去重写它们。
ByteBuf里面的操作为数组操作,里面维护着两个索引readerIndexwriteIndex,具体可以参看NIO中Buffer例子:
Java Netty 学习(二) - NIO基础知识Buffer
所以,由于使用的内存是通过JNI方式申请的内存空间,这些空间JVM是不会进行处理的,所以当使用完之后,需要注意手动释放,否则易造成内存泄漏。
当使用ChannelInboundHandler.channelReadChannelOutboundHandler.write时候,都需要保证没有任何的资源泄漏, 所以在完全使用完某个ByteBuf后,调整其引用计数是很重要的。
例如如下代码:

@Sharable
public class DiscardInboundHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ReferenceCountUtil.release(msg);
    }
}

write方法

@Sharable
public class DiscardOutboundHandler
    extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx,
        Object msg, ChannelPromise promise) {
        ReferenceCountUtil.release(msg);
        promise.setSuccess();  // 表示消息已被处理
    }
}

看ReferenceCountUtil的release方法:

    public static boolean release(Object msg) {
        if (msg instanceof ReferenceCounted) {
            return ((ReferenceCounted) msg).release();
        }
        return false;
    }

ReferenceCountedrelease方法,则是将reference count-1,当count=0时,则调用deallocates将内存对象回收
另外, 可以用Netty提供的ResourceLeakDetector,他将对你的应用程序的缓冲区分配做大约1%的采样来检测内存泄漏,相关开销较小。
在每次创建ByteBuf时,创建一个虚引用对象W指向该ByteBuf对象,如果正常调用release()操作,则将计数器建议,减1;如果ByteBuf对象不再使用(没有其他引用)但没有调用release()操作,则GC时虚引用对象A被加入ReferenceQueue中,通过判断队列是否为空,即可知道是否存在内存泄露。

ChannelHandler流转

那么ChannelHandlerChannelPipeline中是如何流转的呢?
先看下图,ChannelInboundHanler和ChannelOutboundHandler:
在这里插入图片描述
消息从InboundHandlder一端入站,而从OutboundHandler一端出站。
另一方面, ChannelHandlerChannelHandlerContextChannel之间的关系如下图所示
在这里插入图片描述

在写法中,调用ChannelPipeline的add*方法,将handler加入,同时不同类型的handler执行的事件则不同。

参考:

  1. Netty In Action
  2. http://calvin1978.blogcn.com/articles/netty-leak.html
  3. https://blog.csdn.net/yangguosb/article/details/80138719
  4. http://damacheng009.iteye.com/blog/2013657
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值