概述
Netty中ChannelPipeline与Channel的对应关系是一一对应,也就是每个Channel中有且仅有一个ChannelPipeline,可以通过Channel获取唯一的ChannelPipeline,也可以通过ChannelPipeline获取唯一的Channel。

由上图可得,每个Channel维护了一个ChannelPipeline,而ChannelPipeline中又维护了一个由ChannelHandlerContext组成的双向链表,这个链表的表头是HeadContext,链表的末尾是TailContext,并且每个ChannelHanderContext中又关联了一个ChannelHandler。
HeadContext和TailContext并没有包含ChannelHandler,那是因为HeadContext和TailContext继承了AbstractChannelHandlerContext同时实现了ChannelHandler接口,所以它们有了Context和Handler双重属性。 就是HeadContext和TailContext以继承的形式实现这双重属性。


ChannelInitializer的添加
从上面我们知道,最开始的时候ChannelPipeline含有两个ChannelHandlerContext(HeadContext、TailContext),但是这个Pipeline不能实现什么特殊功能,因为我们还没有给他添加自定义的ChannelHandler,通常来说,我们在初始化Bootstrap时,会添加我们自定义的ChannelHandler。
实际上添加到Pipeline里面的是ChannelHandlerContext,只是每个对象维护了一个ChannelHandler。
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//这里添加自定义的。
pipeline.addLast(new ChatClientHandler(nickName));
}
});
在调用handler方法时,传入了ChannelInitializer对象,它提供了一个initChannel方法给我们进行重写,来进行初始化ChannelHandler,addLast表示在pipeline中的链表的TailContext之前添加一个ChannelHandler,用于自定义的功能实现。

这个是ChannelInitializer的类图,可以看出,他仅实现了ChannelInboundHandler接口。

上图是DefaultHandlerContext类的内容,可以看到有两个方法isInbound和isOutbound方法,这两个方法的逻辑是:当一个 handler 实现了 ChannelInboundHandler 接口,则 isInbound 返回 true;类似地,当一个handler 实现了 ChannelOutboundHandler 接口,则 isOutbound 就返回 true。而这两个 boolean 变量会传递到父类AbstractChannelHandlerContext 中,并初始化父类的两个字段:inbound 与 outbound。
并根据上面的ChannelInitializer的类关系图可得,ChannelInitializer的Inbound属性是true,Outbound属性是false。
addLast源码:
private void addLast0(AbstractChannelHandlerContext newCtx) {
//定位到TailContext的上一个Context,如果是第一次添加,那就是HeadContext
AbstractChannelHandlerContext prev = tail.prev;
//假如是第一次添加,那么把要加入的ChannelContext的前置节点赋值为HeadContext
newCtx.prev = prev;
//把要加入的ChannelContext的后置节点赋值为TailContext
newCtx.next = tail;
//如果是第一次添加,把HeadContext的后置节点赋值为新节点
prev.next = newCtx;
//把Tail节点的前置节点赋值为新结点。
tail.prev = newCtx;
//总的来说就是新加入的ChannelContext会在尾部加入,尾部指的是TailContext之前的节点,因为Pipeline的HeadContext和TailCOntext的位置总是不变的,一个在表头,一个在表尾。
}
自定义Handler的添加过程:按照上面代码,把注释去掉。
- 通过hand方法定义一个ChannelInitializer Handler到Pipeline中。此时的图如下:

- 然后执行ChannelInitializer的initChannel方法,添加自定义的ChannelHandler,添加一个MyNettyChannelHandler。

就是在TailContext前添加,保证TailContext是最后的节点。 - 如果继续添加以此类推。
- 最后,initChannel方法执行完成添加之后,会在finally代码块里把ChannelInitializer自身删除。

上面方法是ChannelInitializer的方法。
所以最后的双向链表了只有HeadContext 之后是一些在initChannel方法中添加的自定义Channel,最后是TailContext,ChannelInitializer会被删除。
ChannelHandler的命名规则:
ChannelPipeline.addXXX都有一个重载版本:
比如
ChannelPipeline addLast(String name, ChannelHandler handler);
第一个参数指定添加的 handler 的名字(更准确地说是 ChannelHandlerContext 的名字,说成 handler 的名字更便于理解)。
public final ChannelPipeline addLast(String name, ChannelHandler handler) {
return this.addLast((EventExecutorGroup)null, name, handler);
}
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized(this) {
//检查
checkMultiplicity(handler);
//会通过这个filterName方法进行handler名字的检查或者生成名字,如果名字重复,就会抛出异常,具体在下面
newCtx = this.newContext(group, this.filterName(name, handler), handler);
this.addLast0(newCtx);
if (!this.registered) {
newCtx.setAddPending();
this.callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
public void run() {
DefaultChannelPipeline.this.callHandlerAdded0(newCtx);
}
});
return this;
}
}
this.callHandlerAdded0(newCtx);
return this;
}
private String filterName(String name, ChannelHandler handler) {
if (name == null) {
//如果没有传递handler的名字,就使用默认命名规则
return this.generateName(handler);
} else {
//否则就进行检查。
this.checkDuplicateName(name);
return name;
}
}
//名字重复检查
private void checkDuplicateName(String name) {
if (this.context0(name) != null) {
throw new IllegalArgumentException("Duplicate handler name: " + name);
}
}
//默认的命名规则:默认命名的规则很简单,就是用反射获取 handler 的 simpleName 加上"#0",代码就不解释了,源码在这里。
private String generateName(ChannelHandler handler) {
Map<Class<?>, String> cache = (Map)nameCaches.get();
Class<?> handlerType = han

本文围绕Netty展开,介绍了ChannelPipeline与Channel的对应关系,阐述了ChannelInitializer的添加及ChannelHandler的命名规则。重点讲解了Pipeline的事件传播机制,包括Inbound和Outbound的不同流向及传播方式,并通过测试验证。还提及了ChannelHandler与ChannelHandlerContext的关系、Channel的生命周期等知识。
最低0.47元/天 解锁文章
326





