* <pre>
* I/O Request
* via {@link Channel} or
* {@link ChannelHandlerContext}
* |
* +---------------------------------------------------+---------------+
* | ChannelPipeline | |
* | \|/ |
* | +---------------------+ +-----------+----------+ |
* | | Inbound Handler N | | Outbound Handler 1 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* | | \|/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler N-1 | | Outbound Handler 2 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ . |
* | . . |
* | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
* | [ method call] [method call] |
* | . . |
* | . \|/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler 2 | | Outbound Handler M-1 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* | | \|/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler 1 | | Outbound Handler M | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* +---------------+-----------------------------------+---------------+
* | \|/
* +---------------+-----------------------------------+---------------+
* | | | |
* | [ Socket.read() ] [ Socket.write() ] |
* | |
* | Netty Internal I/O Threads (Transport Implementation) |
* +-------------------------------------------------------------------+
* </pre>
以上是Netty作者在ChannelPipeLine的注释给出的图,可见在ChannelPipeline中维护这一个双向链表。在ChannelPipeLine中,具体处理相关通知请求的成员成员称为ChannelHandlerContext,也是pipeline中双向链表的成员。
而在其中的又根据是请求还是通知分为不同的处理模式。由图可得,当作位通知请求需要去通知上一级的channel的时候,就需要由图中的底向上的通过调用fireIN_EVT()来在双向链表中传递。并由图中的序号可见,在这里的inbound contexthandler传递,并从双线链表的头部开始传递。
而请求的操作则是有挺大的差异,虽然都是通过fireIN_EVT()方法在双向链表当中传递,但是由图可知,是从图中自顶网下传递的,而由此可见,请求是从链表的末尾开始传递请求的。处理请求的成员则是outbound contexthandler。
在注释中,也给出了相应的例子来理解上面的内容。
* <pre>
* {@link ChannelPipeline} p = ...;
* p.addLast("1", new InboundHandlerA());
* p.addLast("2", new InboundHandlerB());
* p.addLast("3", new OutboundHandlerA());
* p.addLast("4", new OutboundHandlerB());
* p.addLast("5", new InboundOutboundHandlerX());
* </pre>
* In the example above, the class whose name starts with {@code Inbound} means it is an inbound handler.
* The class whose name starts with {@code Outbound} means it is a outbound handler.
* <p>
* In the given example configuration, the handler evaluation order is 1, 2, 3, 4, 5 when an event goes inbound.
* When an event goes outbound, the order is 5, 4, 3, 2, 1. On top of this principle, {@link ChannelPipeline} skips
* the evaluation of certain handlers to shorten the stack depth:
* <ul>
* <li>3 and 4 don't implement {@link ChannelInboundHandler}, and therefore the actual evaluation order of an inbound
* event will be: 1, 2, and 5.</li>
* <li>1 and 2 don't implement {@link ChannelOutboundHandler}, and therefore the actual evaluation order of a
* outbound event will be: 5, 4, and 3.</li>
* <li>If 5 implements both {@link ChannelInboundHandler} and {@link ChannelOutboundHandler}, the evaluation order of
* an inbound and a outbound event could be 125 and 543 respectively.</li>
* </ul>
以上的给出的例子构造了一个含有顺序五个context handler成员的pipeline,而其中1和2分别为InBoundHandler,3和4分别为OutBoundHandler,5则既为InBoundHandler也为OutBoundHandler。当这样的一个pipeline在在处理inbound event的时候,处理的顺序则为1,2,5.而在处理outbound event的时候,处理得当顺序则为5,4,3.可以在这里看到pipeline对于不同请求通知的调用形式。
每一个Channel都对应着一个ChannelPipeline,并且每次在Channel的创建时,ChannelPipeline都会被自动创建出来。
可以看到,DefaultChannelPipeline的构造方法。
public DefaultChannelPipeline(AbstractChannel channel) {
if (channel == null) {
throw new NullPointerException("channel");
}
this.channel = channel;
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
在构造方法的一开始就创建了一个tailContext和headContext作为双向链表的头部和尾部。也就是在这里就已经完成创建了双向链表。但是,其实在接下来不管怎么调用addFirst或者addLast,双向链表的头部和尾部都是以上这两者。
可以看到addFirst()方法,也就是往双向链表顶部添加contextHandler的方法。
public ChannelPipeline addFirst(EventExecutorGroup group, final String name, ChannelHandler handler) {
synchronized (this) {
checkDuplicateName(name);
AbstractChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
addFirst0(name, newCtx);
}
return this;
}
首先,会对添加进来进来的handler的name进行判断,是否已经添加过,通过checkDuplicateName()方法来进行判断。
private void checkDuplicateName(String name) {
if (name2ctx.containsKey(name)) {
throw new IllegalArgumentException("Duplicate handler name: " + name);
}
}
在pipeline中,存在一个名为name2ctx的map来为添加进来的handler进行去重,保证没有重复名字的handler添加进handler。
之后,则会将准备添加进来的handler生成包装类AbstractChannelHandlerContext。
生成包装类的过程中,主要还是为了设置该handler的inbound还是outbound属性。
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutorGroup group, String name, ChannelHandler handler) {
super(pipeline, group, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
}
private static boolean isInbound(ChannelHandler handler) {
return handler instanceof ChannelInboundHandler;
}
private static boolean isOutbound(ChannelHandler handler) {
return handler instanceof ChannelOutboundHandler;
}
可以看到,这里根据是否继承的是Outbound还是Inbound进行了判断并且作为属性作为了AbstractChannelPipeline的构造方法的成员用来设置inbound和outbound的属性。
在完成了包装类的创建之后,进入addFirst0()继续添加handler。
private void addFirst0(String name, AbstractChannelHandlerContext newCtx) {
checkMultiplicity(newCtx);
AbstractChannelHandlerContext nextCtx = head.next;
newCtx.prev = head;
newCtx.next = nextCtx;
head.next = newCtx;
nextCtx.prev = newCtx;
name2ctx.put(name, newCtx);
callHandlerAdded(newCtx);
}
首先会通过checkMultiplicity()方法,来防止同一个handler包装类的重复添加。
private static void checkMultiplicity(ChannelHandlerContext ctx) {
ChannelHandler handler = ctx.handler();
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
if (!h.isSharable() && h.added) {
throw new ChannelPipelineException(
h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times.");
}
h.added = true;
}
}
在判断未添加过之后,会给added属性设为true,防止在后面重复往pipeline添加同一个handler。
之后,就是简单的双向链表操作,以保证head仍旧在双向链表的顶端。
以上就是往pipeline中双向链表头部添加handler的例子。
接下来可以看到pipeline中双向链表的事件传递。
当一个Channel完成了端口与selector之间的注册的时候,会调用pipeline的fireChannelRegistered()方法,通知通道已经注册完成的通知的目的。
public ChannelPipeline fireChannelRegistered() {
head.fireChannelRegistered();
return this;
}
可以见到,是从双向链表的头开始的,也就是headContext开始向链表后面的handler成员开始,那么会直接调用head的fireChannelRegistered()方法。这一方法实现在AbstractChannelHandlerContext中,也就是说,基本所有的handler都会实现这一方法。
public ChannelHandlerContext fireChannelRegistered() {
final AbstractChannelHandlerContext next = findContextInbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new OneTimeTask() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
return this;
}
首先,会通过findContextInbound()方法寻找下一个用来调用的handler。
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
可以看到从链表中寻找下一个作为inbound属性为true的handler,这也印证了刚才注释所解释的内容。从双向链表头部开始调用的事件通知是有inbound属性为true的handler来参与相应的实现的。
在找到了相应的inbound属性为true的handler,则会通过invokeChannelRegistered(0方法来调用这个handler的channelRegistered()方法。
private void invokeChannelRegistered() {
try {
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
}
inboud为true属性的handler首先要继承了ChannelInboundHandlerAdapter类,如果没有特地重写了channelRegistered()方法,则会直接实现该类的方法。
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelRegistered();
}
很简单的方法,默认并没有任何实现,而是直接与head的handler一样调用双向链表下一个handler的channelRegistered()方法,这样,也保证完成了双向链表中的事件传递。