pipeline.fireChannelRead
原理图
源码分析一ChannelPipeline.fireChannelRead
- 调用invokeChannelRead,传入headContext
- 指定线程为io线程还是handler自身业务线程
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
static void invokeChannelRead(final AbstractChannelHandlerContext next, final Object msg) {
ObjectUtil.checkNotNull(msg, "msg");
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(msg);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(msg);
}
});
}
}
- 如果channel已经添加到责任链上,则执行channelRead
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
源码分析一SimpleChannelInboundHandler
- 判断消息是否为SimpleChannelInboundHandler指定泛型
- 是指定泛型触发处理【开发人员于channelRead0手动调用ctx.fireChannelRead(msg)】
- 不是指定泛型触发下一个入站handler处理
- 对于bytebuf通过引用计数算法释放内存
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
step-1: 判断消息是否为SimpleChannelInboundHandler<I>指定泛型
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg;
step-1.1: 是指定泛型触发处理
channelRead0(ctx, imsg);
} else {
step-1.2: 不是指定泛型触发下一个入站handler处理
release = false;
ctx.fireChannelRead(msg);
}
} finally {
如果是ReferenceCounted类型 则通过引用计数算法释放内存
if (autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}
调用链路与线程原理图
- 左边为channel实际初始化后形成的handler链
- 每一个context都持有一个handler与线程[如果没有则使用workerEventLoop线程]
- 通过SINGLE_EVENTEXECUTOR_PER_GROUP参数可以使得所有context的业务线程被指定为同一个线程
- context分为inbound和outbound两类,inbound处理入站消息,outbound处理出站消息
总结
- 当IO事件通过eventLoop线程处理后,会通过netty unsafe触发netty框架的链式处理,handler链可以使用自带线程,如无线程配置则使用workereventLoop线程
- 如果使用自带线程,推荐配置SINGLE_EVENTEXECUTOR_PER_GROUP参数使得每一个handlercontext线程相同
方法名 | 描述 |
---|
ctx.fireChannelRead(msg) | 从当前ctx开始寻找链式handler的下一个节点 |
ctx.pipeline().fireChannelRead(msg) | 从链首开始调用,pipeline本身持有的也是headContext |
ctx.channel().write(msg) | 调用pipeline.write, 从链尾开始出站 |
ctx.pipeline().write(msg) | 从链尾开始出站 |
ctx.write(msg) | 逆向查找下一个出站context |