本篇文章来分析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
里面的操作为数组操作,里面维护着两个索引readerIndex
和writeIndex
,具体可以参看NIO中Buffer
例子:
Java Netty 学习(二) - NIO基础知识Buffer
所以,由于使用的内存是通过JNI方式申请的内存空间,这些空间JVM是不会进行处理的,所以当使用完之后,需要注意手动释放,否则易造成内存泄漏。
当使用ChannelInboundHandler.channelRead
和ChannelOutboundHandler.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;
}
而ReferenceCounted
的release
方法,则是将reference count-1,当count=0时,则调用deallocates
将内存对象回收
另外, 可以用Netty提供的ResourceLeakDetector,他将对你的应用程序的缓冲区分配做大约1%的采样来检测内存泄漏,相关开销较小。
在每次创建ByteBuf时,创建一个虚引用对象W指向该ByteBuf对象,如果正常调用release()操作,则将计数器建议,减1;如果ByteBuf对象不再使用(没有其他引用)但没有调用release()操作,则GC时虚引用对象A被加入ReferenceQueue中,通过判断队列是否为空,即可知道是否存在内存泄露。
ChannelHandler流转
那么ChannelHandler
在ChannelPipeline
中是如何流转的呢?
先看下图,ChannelInboundHanler和ChannelOutboundHandler:
消息从InboundHandlder一端入站,而从OutboundHandler一端出站。
另一方面, ChannelHandler
、ChannelHandlerContext
、Channel
之间的关系如下图所示
在写法中,调用ChannelPipeline的add*方法,将handler加入,同时不同类型的handler执行的事件则不同。
参考: