Netty源码分析(四):关于ChannelPipeline和addLast
ChannelPipeline
1.架构设计:
Channel都有且仅有一个ChannelPipeline与之对应,Channel包含了ChannelPipeline,ChannelPipeline内部包含了N个handler,每一个handler都是由一个线程去执行;
ChannelPipeline内部维护了一个由ChannelHandlerContext组成的双向链表,头为HeadContext,尾为TailHandler(双向链表为自己写的,而不是使用JDK的链表,为了轻量级),并且每个ChannelHandlerContext中又关联着一个ChannelHandler:
2.ChannelInboundHandler和ChannelOutboundHandler
设计技巧:tail是Inbound为true,head是outbound为true,可以理解为尾进头出,就是由inbound和outbound变量来控制的,这也是使用者要添加handler则使用addLast方法的原因;不过注意这里不能理解成队列,因为是有顺序的
inbound设计
in和out是相对channel、即程序来说的;out即往外面发送东西,in即接收东西。
两者都是异步发生、在线程中响应的,通过线程去执行。
out为请求型的动作,由业务程序来触发;in为响应型的动作,由Netty来触发,业务程序被动接收。
什么时候使用inbound呢?
实现了接口后,可以看到需要实现channelRead0方法,这个方法使用者无须调用,因为是由netty调用的,此接口的其他方法也是如此,因此做业务过程中,最多的就是用inbound,而outbound需要手动去调用
connect方法中,层层调用,到达AbstractChannel.AbstractUnsafe#register0方法
//AbstractChannel.AbstractUnsafe#register0
private void register0(ChannelPromise promise) {
doRegister();//将SocketChannel注册到selector中,之前已经分析过
pipeline.fireChannelRegistered();//重点分析
}
//DefaultChannelPipeline#fireChannelRegistered
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);//传入head
return this;
}
//AbstractChannelHandlerContext#invokeChannelRegistered(AbstractChannelHandlerContext)
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {//开启一个线程来执行
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
private void invokeChannelRegistered() {
((ChannelInboundHandler) handler()).channelRegistered(this);
}
//DefaultChannelPipeline.HeadContext#channelRegistered
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {//
invokeHandlerAddedIfNeeded();
ctx.fireChannelRegistered();
}
//AbstractChannelHandlerContext#fireChannelRegistered
public ChannelHandlerContext fireChannelRegistered() {
invokeChannelRegistered(findContextInbound());
return this;
}
//AbstractChannelHandlerContext#invokeChannelRegistered(AbstractChannelHandlerContext)
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
可以看到,到了这里,即第七段,代码就陷入了循环调用(3段->4段>5段>6段->7段即3段->4段…)
所以看看第六段代码调用的findContextInbound():
//AbstractChannelHandlerContext#findContextInbound
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
设计技巧:只要为inbound就返回,即一直调用,直到等于自己,就全部结束;
这里用inbound来判断,因为head的inbound为false
注:
链路调用从tail开始,一直往下next,调了head之后回到本身,终止调用;这是一个环形链路
fire才会触发下一个handler
addLast
addLast内部也有多个重载方法,类似Spring的beanFactory给每个bean起名字,为类名首字母小写,这里同样会起名字,名字我们看源码来分析:
private String generateName(ChannelHandler handler) {
Map<Class<?>, String> cache = nameCaches.get();//会有一个缓存
Class<?> handlerType = handler.getClass();
String name = cache.get(handlerType);
if (name == null) {
//return StringUtil.simpleClassName(handlerType) + "#0";
//假设handler名字为PdcHandler,则name为PdcHandler#0
name = generateName0(handlerType);
cache.put(handlerType, name);
}
return name;
}
如果自己起名,则不能重复