关于Netty中重要的组件 EventLoop, Pipeline , Handler , FastThreadLocal ,ByteBuf ,Allocator等组件源码分析。
1. DefaultChannelPipeline 组件
Pipeline是Netty异步处理的核心,开发者实现将Handler(处理回调)注册到Pipeline中。 Pipeline注册到EventLoop中,
当io.netty.channel.Channel监听到了IO事件以后,会将Message传播到 Pipeline中。
DefaultChannelPipeline是 ChannelPipeline的默认实现。
整个Pipeline就是责任链模式。
责任链设计思路的核心就是:每个handler都处理自己份内的事儿,当前的handler处理消息完毕,event传递到下一个handler。
使用责任链值得注意的点: Handler的先后顺序有讲究,需要特别注意前后Handler对Message的传递情况。
Netty有入站、出站2种event区分。 “引用计数对象”的retain 和 release。
1.1 fireChannelRead()
方法
Pipeline的fireChannelRead()
和 context 的fireChannelRead()
传递源头是不同的。
pipeline 从head传播
context 从下一个handler开始传播
每一个io.netty.channel.Channel都有独立的Pipeline。每一个Pipeline的Handler链都有 Head 和 Tail节点。
pipeline 从head传播:
//DefaultChannelPipeline.java
@Override
public final ChannelPipeline fireChannelRead(Object msg) {
//传入了head节点。
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
context 从下一个handler开始传播:
//AbstractChannelHandlerContext.java
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
//从下一个支持channel_read 事件的handler,然后开始传播
//findContextInbound是netty内部使用的,用于过滤 handler的
//每个handler可以设置自己支持的event callback。
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
1.2 TailContext 组件
DefaultChannelPipeline的尾节点类型 TailContext。
在Handler消费一个ReferenceCounted对象时,必须要先调用retain()
防止它被意外回收。
这么做的原因在于,DefaultChannelPipeline的尾节点,默认会释放1次ReferenceCounted对象的引用。
//TailContext
protected void onUnhandledInboundMessage(Object msg) {
try {
logger.debug(
"Discarded inbound message {} that reached at the tail of the pipeline. " +
"Please check your pipeline configuration.", msg);
} finally {
ReferenceCountUtil.release(msg);
}
}
2. DefaultChannelHandlerContext 组件
默认的HandlerContext对象。
由于Pipeline的存在,在这条链上的Handler都有上下游的关系。 而有些Handler是 Shareable的,向上下游传播状态信息需要则需要借助 Context。
万幸的是Netty提供了这样的功能(当然,对于非Shareable的Handler , Netty更推荐成员内部类的方式,它更直观简洁)。
使用 attr()
在上下文之间传递状态。
//DefaultChannelHandlerContext.java
public <T> Attribute<T> attr(AttributeKey<T> key) {
//实际上是 io.netty.channel.Channel.attr() 在工作
return channel().attr(key);
}
context的attr绑定在了 Channel中。