一,概念
能够被selector监控的IO事件有四种,分别是SelectionKey里面的 可读,可写 ,连接, 接收。四种。
在NioEventLoop里面有selector对事件进行监听,然后dispatch(派发)给handler。Netty中handler分为两类,一种是通道入站处理器Inbound; 另外一种是通道出战处理器Outbound;
两者都继承了ChannelHandler。
举例:比如EventLoop查询到了OP_READ事件,那么它就发送给入站处理器:ChannelInboundHandler通道入站处理器,然后调用它的read()方法。从通道中读取数据。这两个接口有它的默认实现XXXAdapter,称为xxx处理适配器。实现了入站/出战的基本功能。我们自己的业务处理器只需要继承这两个处理适配器就可以了,不需要从0开始构建。
在reactor反应器模式中,反应器将查询到的IO事件,分发到Handler业务处理器,由handler处理器完成IO操作和业务处理。整个处理包括:从通道读取数据包,数据包解码,业务处理,目标数据编码,把数据包写回通道。最后由通道发送。
数据包解码,业务处理属于 入站处理器的工作,目标数据编码,把数据写回通道是 出站处理器的工作。
二,源码分析
1.ChannelHandler
对于handler来说,它的继承关系图,相对来简单一点,但是由于handler是主要扩展和定制点,所以它的子类种类繁多,功能各异,系统的channelHandler主要分类如下:
1.ChannelPipline的系统channelHandler,用于IO操作和对事件进行预处理,对用户不可见,主要包括HeadHandler,TailHandler。
2.编解码ChannelHandler,包括ByteToMessageCodec,MessageToMessageDecoder等,这些编解码类本身又包括多种子类
3.其他系统功能性ChannelHandler,包括流量整型handler,读写超时handler,日志handler等。
顶层接口源码:
public interface ChannelHandler {
//handler本身被添加道channelPipline的时候调用
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
//移除时调用
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
实现类:ChannelHandlerAdapter
它并没有实现接口的两个add,remove方法,只是一个空的,交给子类重写。但是里面有一个重要的方法,用来提升性能的,减少线程间的竞争。
public abstract class ChannelHandlerAdapter implements ChannelHandler {
...
public boolean isSharable() {
Class<?> clazz = getClass();
Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
Boolean sharable = cache.get(clazz);
if (sharable == null) {
sharable = clazz.isAnnotationPresent(Sharable.class);
cache.put(clazz, sharable);
}
return sharable;
}...
}
2.实现类
2.1 ChannelInboundHandler
实现类,有两个常用的,
1.SimpleChannelInboundHandler
它的两个字段:
private final TypeParameterMatcher matcher; //类型参数匹配器
private final boolean autoRelease; //是否自动解析
我们之所以选择handler直接继承这个类,就是因为可以自动进行匹配,然后对消息进行转换。
核心方法:ChannelRead()
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg; //通过范型,将得到的message,转换成指定pojo(也就是范型)
channelRead0(ctx, imsg); //调用子类实现方法对 结果进行处理
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}
2.ChannelInitializer
用来连接用户自定义的handler与pipline,
Channel在初始化的时候,调用pipline,添加handler,而我们的ChannelInitializer就会作为handler加入到pipline链路中,但是这里它的initChannel()方法又装了handler,所以这里先不要着急,在handler真正放入pipline中的时候,会调用handlerAdded()方法
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) { //确保已经注册
if (initChannel(ctx)) { //这里会调用下面的方法
removeState(ctx);
}
}
}
我们看它的initChannel()方法
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) { // 将context加入到集合中。
try {
initChannel((C) ctx.channel()); //再次初始化,这个方法,就是子类的方法,也就是我们手写的方法了。
} ... finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) { //最后把自己从pipline中移除。
pipeline.remove(this);
}
}
}
所以Channelnitializer是作为pipline和Handler之间的桥梁,它最开始把自己加入进去了,最后又把自己删除了!
2.2 ChannelOutboundHandler
主要方法,和inbound完全不一样,
它主要是对消息进行response,回传响应,所以需要bind()地址,还要进行连接,等等操作。最后需要把消息写入到bytebuf中,通过channel传回。
而它的实现类ChannelOutboundHandlerAdapter,将所有方法,都调用了一遍参数context的同名方法,比如:
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.deregister(promise);
}
所以方法都是如此。
参考链接:https://www.jianshu.com/p/a9bcd89553f5
https://www.jianshu.com/p/96a50869b527
《netty权威指南》