netty 的所有handler构建通常是通过引到类创建的时候带进去的。代码一般如下:
//创建引导
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup(10);
//
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
.childHandler(new NettyServerInitializer());
//实现具体的handler
public class NettyServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,
0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
//
pipeline.addLast(new MyServerHandler());
pipeline.addLast(new MyServerHandler2());
log.info("add new channl {}", socketChannel);
}
}
上面的代码可以看到 ,处理我们的业务过程中,需要很多handler来处理各种不同场景的需求。包括编码解码,处理io,处理业务等。那么问题来了: 通过addLast()
加到处理链路的handler到底是什么执行顺序?
首先netty分为入站和出站处理器,通过继承不同的接口来区分。
内部通过DefaultChannelPipeline
来维护这个链表。我们看下addLast代码
//来自DefaultChannelPipeline
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
可以看出来,所有的入站核出站的handler是维护在一个链表中。并且这个context默认已经由两个默认hander:headContext和tailContext
下图中,使用紫色代表入站Context,橙色代表出站Context。

这个图代表:
Tail是出站执行流程的启动点,但是,它最后一个入站处理器。
Hearder,是入站流程的启动起点,但是,它最后一个出站处理器。
也就是说:
处理入站事件 从head找到tail。属于顺序查找
处理出站事件 从tail找到head。属于倒序查找
另外: 执行入站事件(比如读事件),会从AbstractChannelHandlerContext#findContextInbound()
查找所有的事件。而出站事件这是走的findContextOutbound
.两个方法代码如下所示:
//入站和出站查找
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
private AbstractChannelHandlerContext findContextOutbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
可以看到。channel自身所关联的content上下文已经维护了一个双向链表。
在查询入站事件的时候一直查找next
,查找出站事件的时候,一直查找prev
总结
入站: 从header到tail ,顺序。 初始化的时候addLast a,b,c三个handler。则执行顺序是 a,b,c
出站: 从tail找到header,倒序。初始化的时候addLast d,e,f 三个handelr。则执行顺序是f,e,d
网友评论