删除channelHandler主要分为以下几个步骤:
1、找到节点
2、链表里面删除找到的那个节点
3、回调删除handler事件。
我们从一个简单的用户代码开始分析:
public class AuthHandler extends SimpleChannelInboundHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf password) throws Exception {
if (paas(password)) {
ctx.pipeline().remove(this);
} else {
ctx.close();
}
}
private boolean paas(ByteBuf password) {
return false;
}
}
这是服务端一个验证用户的代码,如果通过了验证,就把这个authHandler删除掉(如果没有通过验证,就关闭这个连接),点进去remove开始分析:
@Override
public final ChannelPipeline remove(ChannelHandler handler) {
remove(getContextOrDie(handler));
return this;
}
handler是被handleContext包装的。
上面的getContextOrDie名字取得有意思,通过handler找到context,没有找到就抛出异常:
private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
if (ctx == null) {
throw new NoSuchElementException(handler.getClass().getName());
} else {
return ctx;
}
}
看看怎么找到context哦:
@Override
public final ChannelHandlerContext context(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
AbstractChannelHandlerContext ctx = head.next;
for (;;) {
if (ctx == null) {
return null;
}
if (ctx.handler() == handler) {
return ctx;
}
ctx = ctx.next;
}
}
粗暴得很,居然是从head的下一个节点开始找,找ctx的head和当前的一样,就返回。
回去remove方法:
private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
assert ctx != head && ctx != tail;
synchronized (this) {
remove0(ctx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we remove the context from the pipeline and add a task that will call
// ChannelHandler.handlerRemoved(...) once the channel is registered.
if (!registered) {
callHandlerCallbackLater(ctx, false);
return ctx;
}
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerRemoved0(ctx);
}
});
return ctx;
}
}
callHandlerRemoved0(ctx);
return ctx;
}
这个比较有意思啦,需要assert ctx != head && ctx != tail;也就是保证不是head或者tail,在netty里面,这两个节点是不能删除的。
同步代码块里面,remove0()是执行真正的逻辑删除:
private static void remove0(AbstractChannelHandlerContext ctx) {
AbstractChannelHandlerContext prev = ctx.prev;
AbstractChannelHandlerContext next = ctx.next;
prev.next = next;
next.prev = prev;
}
越看到后面,越觉得源码没有挑战性,这段逻辑就是双向链表的删除逻辑。
至于callHandlerRemoved0方法,就是回调删除事件。
点击进去callHandlerRemoved0方法:
private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) {
// Notify the complete removal.
try {
try {
ctx.handler().handlerRemoved(ctx);
} finally {
ctx.setRemoved();
}
} catch (Throwable t) {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));
}
}
首先ctx.handler().handlerRemoved(ctx);就是回调删除事件,然后通过ctx.setRemoved();设置删除的状态:
final void setRemoved() {
handlerState = REMOVE_COMPLETE;
}