标签:html 实现 next ref png handle htm 知识点 register
Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)
在上一节 接收数据:自适应缓冲区和连接读是为了解决什么问题 中,我们知道 NioEventLoop 不断的轮询,接收 OP_READ 事件;然后将读取到的数据通过 pipeline.fireChannelRead(byteBuf) 传播出去。所以业务的处理实际上是在 pipeline 的 channelRead() 上处理的,本小节也主要关注事件是在 pipeline 中传播行为。
事件在 pipeline 中的传播,需要关注事件的传播行为和执行线程:
主线其实在上一节已经讲了,在读取数据时会触发 pipeline.fireChannelRead(byteBuf) 把读取到的数据传播出去。我们现在重点分析事件是如何在 pipeline 中传播的。
Handler 执行资格:
(1)处理业务本质
数据在 pipeline 中所有的 ChannelInboundHandler 的 channelRead() 执行。并且执行可以被中断。
(2)业务处理线程
默认处理线程是 Channel 绑定的 NioEventLoop 线程,也可以设置其他线程:
pipeline.addLast(new UnorderedThreadPoolEventExecutor(10), serverHandler);
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
说明: 可以看到 fireChannelRead 是从 head -> tail 一直身后传播。
pipeline 中一旦被中间某个 Handler 执行,则传播行为中断。如果需要继续执行下去,则需要主动调用 ctx.fireChannelRead。
// AbstractChannelHandlerContext
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
说明: 可以看到要么执行 channelRead 方法,要么执行 fireChannelRead 方法直到找到一个对应的 Handler 为止。如果不主要调用 ctx.fireChannelRead,则传播行为会中断。
可能会奇怪,通过 findContextInbound() 不是找到对应的 Handler 了,为什么还需要通过 invokeHandler() 再判断一次?其实,findContextInbound 方法是查找有 channelRead 的 Handler,而 invokeHandler 方法则是判断这个 Handler 是否被删除了。
每个 Handler 都是在自己对应的 executor 中执行,默认为 NioEventLoop 线程。当然也可以自己指定其它线程。
// AbstractChannelHandlerContext
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
说明: 我们需要重点关注业务的执行线程,因为如果业务占用时间过长,会影响 Netty IO 的吞吐率。
Netty 将每个 Handler 都包装成 ChannelHandlerContext 添加到 ChannelPipeline 中。虽然说 ChannelPipeline 也就调用 ChannelHandlerContext 中的方法,但 pipeline 会从 head 或 tail 开始遍历,而 ctx 只会从当前 hander 开始遍历。
我们还是拿以下四个 read 对比:channel.read()、pipeline.read()、ctx.read()、unsafe.read()
方法 | 执行顺序 | 功能说明 |
---|---|---|
bind | outbound | unsafe.bind |
connect | outbound | unsafe.connect |
disconnect | outbound | unsafe.disconnect |
close | outbound | unsafe.close |
deregister | outbound | unsafe.deregister |
read | outbound | unsafe.beginRead:注册感兴趣事件 |
write | outbound | unsafe.write |
flush | outbound | unsafe.flush |
exceptionCaught | inbound | ctx.fireExceptionCaught |
channelRegistered | inbound | callHandlerAddedForAllHandlers ctx.fireChannelRegistered |
channelUnregistered | inbound | ctx.fireChannelUnregistered destroy |
channelActive | inbound | ctx.fireChannelActive readIfIsAutoRead:调用unsafe.beginRead |
channelInactive | inbound | ctx.fireChannelInactive() |
channelRead | inbound | ctx.fireChannelRead |
channelReadComplete | inbound | ctx.fireChannelReadComplete readIfIsAutoRead |
userEventTriggered | inbound | ctx.fireUserEventTriggered |
channelWritabilityChanged | inbound | ctx.fireChannelWritabilityChanged |
方法 | 执行顺序 | 功能说明 |
---|---|---|
channelRead | inbound | ReferenceCountUtil.release(msg) |
每天用心记录一点点。内容也许不重要,但习惯很重要!
标签:html 实现 next ref png handle htm 知识点 register
原文地址:https://www.cnblogs.com/binarylei/p/12641011.html