原文链接:https://wangwei.one/posts/netty-pipeline-source-analyse-2.html
前面 ,我们分析了Netty Pipeline的初始化及节点添加与删除逻辑。接下来,我们将来分析Pipeline的事件传播机制。
Netty版本:4.1.30
inBound事件传播
示例
我们通过下面这个例子来演示Netty Pipeline的事件传播机制。
public class NettyPipelineInboundExample {
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup(1);
ServerBootstrap strap = new ServerBootstrap();
strap.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(8888))
.childOption(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new InboundHandlerA());
ch.pipeline().addLast(new InboundHandlerB());
ch.pipeline().addLast(new InboundHandlerC());
}
});
try {
ChannelFuture future = strap.bind().sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
class InboundHandlerA extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("InboundHandler A : " + msg);
// 传播read事件到下一个channelhandler
ctx.fireChannelRead(msg);
}
}
class InboundHandlerB extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("InboundHandler B : " + msg);
// 传播read事件到下一个channelhandler
ctx.fireChannelRead(msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// channel激活,触发channelRead事件,从pipeline的heandContext节点开始往下传播
ctx.channel().pipeline().fireChannelRead("Hello world");
}
}
class InboundHandlerC extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("InboundHandler C : " + msg);
// 传播read事件到下一个channelhandler
ctx.fireChannelRead(msg);
}
}
通过 telnet 来连接上面启动好的netty服务,触发channel active事件:
$ telnet 127.0.0.1 8888
按照InboundHandlerA、InboundHandlerB、InboundHandlerC的添加顺序,控制台输出如下信息:
InboundHandler A : Hello world
InboundHandler B : Hello world
InboundHandler C : Hello world
若是调用它们的添加顺序,则会输出对应顺序的信息,e.g:
...
ch.pipeline().addLast(new InboundHandlerB());
ch.pipeline().addLast(new InboundHandlerA());
ch.pipeline().addLast(new InboundHandlerC());
...
输出如下信息:
InboundHandler B : Hello world
InboundHandler A : Hello world
InboundHandler C : Hello world
源码分析
强烈建议 下面的流程,自己通过IDE的Debug模式来分析
待netty启动成功,通过telnet连接到netty,然后通过telnet终端输入任意字符(这一步才开启Debug模式),进入Debug模式。
触发channel read事件,从下面的入口开始调用
public class DefaultChannelPipeline implements ChannelPipeline {
...
// 出发channel read事件
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
// 从head节点开始往下传播read事件
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
...
}
调用 AbstractChannelHandlerContext 中的 invokeChannelRead(head, msg)
接口:
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
implements ChannelHandlerContext, ResourceLeakHint {
...
// 调用channel read
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
// 获取消息
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
// 获取 EventExecutor
EventExecutor executor = next.executor();
// true
if (executor.inEventLoop()) {
// 调用下面的invokeChannelRead接口:invokeChannelRead(Object msg)
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
// handler():获取当前遍历到的channelHandler,第一个为HeandContext,最后为TailContext
// 调用channel handler的channelRead接口
((ChannelInboundHandler) handler