Netty源码分析第四章: pipeline

第三节: handler的删除

上一小节我们学习了添加handler的逻辑操作, 这一小节我们学习删除handler的相关逻辑

如果用户在业务逻辑中进行ctx.pipeline().remove(this)这样的写法, 或者ch.pipeline().remove(new SimpleHandler())这样的写法, 则就是对handler进行删除, 我们学习过添加handler的逻辑, 所以对handler删除操作理解起来也会比较容易

我们首先跟到defaultChannelPipeline的remove(handler)的方法中:

public final ChannelPipeline remove(ChannelHandler handler) {
remove(getContextOrDie(handler));
return this;
}

方法体里有个remove()方法, 传入一个 getContextOrDie(handler) 参数, 这个 getContextOrDie(handler) , 其实就是根据handler拿到其包装类HandlerContext对象

我们跟到getContextPrDie这个方法中:

private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
//代码省略
}

这里仍然会通过context(handler)方法去寻找, 再跟进去:

public final ChannelHandlerContext context(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
//从头遍历节点
AbstractChannelHandlerContext ctx = head.next;
for (;;) {
if (ctx == null) {
return null;
}
//找到handler
if (ctx.handler() == handler) {
return ctx;
}
ctx = ctx.next;
}
}

这里我们看到寻找的方法也非常的简单, 就是从头结点开始遍历, 遍历到如果其包装的handler对象是传入的handler对象, 则返回找到的handlerContext

回到remove(handler)方法:

public final ChannelPipeline remove(ChannelHandler handler) {
remove(getContextOrDie(handler));
return this;
}

继续跟到remove方法中:

private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {

    //当前删除的节点不能是head, 也不能是tail
assert ctx != head && ctx != tail; synchronized (this) {
//执行删除操作
remove0(ctx);
if (!registered) {
callHandlerCallbackLater(ctx, false);
return ctx;
} //回调删除handler事件
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerRemoved0(ctx);
}
});
return ctx;
}
}
callHandlerRemoved0(ctx);
return ctx;
}

首先要断言删除的节点不能是tail和head

然后通过remove0(ctx)进行实际的删除操作, 跟到remove0(ctx)中:

private static void remove0(AbstractChannelHandlerContext ctx) {
//当前节点的前置节点
AbstractChannelHandlerContext prev = ctx.prev;
//当前节点的后置节点
AbstractChannelHandlerContext next = ctx.next;
//前置节点的下一个节点设置为后置节点
prev.next = next;
//后置节点的上一个节点设置为前置节点
next.prev = prev;
}

这里的操作也非常简单, 做了一个指针移动的操作, 熟悉双向链表的小伙伴应该不会陌生, 删除节点逻辑大概如下图所示:

4-3-1

回到remove(ctx)方法:

private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {

    //当前删除的节点不能是head, 也不能是tail
assert ctx != head && ctx != tail; synchronized (this) {
//执行删除操作
remove0(ctx);
if (!registered) {
callHandlerCallbackLater(ctx, false);
return ctx;
} //回调删除handler事件
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerRemoved0(ctx);
}
});
return ctx;
}
}
callHandlerRemoved0(ctx);
return ctx;
}

我们继续往下看, 如果当前线程不是eventLoop线程则将回调删除事件封装成task放在taskQueue中让eventLoop线程进行执行, 否则, 则直接执行回调删除事件

跟到callHandlerRemoved0(ctx)方法中:

private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) {
try {
try {
//调用handler的handlerRemoved方法
ctx.handler().handlerRemoved(ctx);
} finally {
//将当前节点状态设置为已移除
ctx.setRemoved();
}
} catch (Throwable t) {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));
}
}

与添加handler的逻辑一样, 这里会调用当前handler的handlerRemoved方法, 如果用户没有重写该方法, 则会调用其父类的方法, 方法体在ChannelHandlerAdapter类中有定义, 我们跟进去

public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

}

同添加handler一样, 也是一个空实现, 这里用户可以通过重写来添加自己需要的逻辑

以上就是删除handler的相关操作

上一节: handler的添加

下一节: 传播inbound事件

Netty源码分析第4章(pipeline)---->第3节: handler的删除的更多相关文章

  1. Netty源码分析第4章(pipeline)---->第2节: handler的添加

    Netty源码分析第四章: pipeline 第二节: Handler的添加 添加handler, 我们以用户代码为例进行剖析: .childHandler(new ChannelInitialize ...

  2. Netty源码分析第4章(pipeline)---->第4节: 传播inbound事件

    Netty源码分析第四章: pipeline 第四节: 传播inbound事件 有关于inbound事件, 在概述中做过简单的介绍, 就是以自己为基准, 流向自己的事件, 比如最常见的channelR ...

  3. Netty源码分析第4章(pipeline)---->第5节: 传播outbound事件

    Netty源码分析第五章: pipeline 第五节: 传播outBound事件 了解了inbound事件的传播过程, 对于学习outbound事件传输的流程, 也不会太困难 在我们业务代码中, 有可 ...

  4. Netty源码分析第4章(pipeline)---->第6节: 传播异常事件

    Netty源码分析第四章: pipeline 第6节: 传播异常事件 讲完了inbound事件和outbound事件的传输流程, 这一小节剖析异常事件的传输流程 首先我们看一个最最简单的异常处理的场景 ...

  5. Netty源码分析第4章(pipeline)---->第7节: 前章节内容回顾

    Netty源码分析第四章: pipeline 第七节: 前章节内容回顾 我们在第一章和第三章中, 遗留了很多有关事件传输的相关逻辑, 这里带大家一一回顾 首先看两个问题: 1.在客户端接入的时候, N ...

  6. Netty源码分析第4章(pipeline)---->第1节: pipeline的创建

    Netty源码分析第四章: pipeline 概述: pipeline, 顾名思义, 就是管道的意思, 在netty中, 事件在pipeline中传输, 用户可以中断事件, 添加自己的事件处理逻辑, ...

  7. Netty源码分析第5章(ByteBuf)---->第10节: SocketChannel读取数据过程

    Netty源码分析第五章: ByteBuf 第十节: SocketChannel读取数据过程 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程: 首先 ...

  8. Netty源码分析第5章(ByteBuf)---->第4节: PooledByteBufAllocator简述

    Netty源码分析第五章: ByteBuf 第四节: PooledByteBufAllocator简述 上一小节简单介绍了ByteBufAllocator以及其子类UnPooledByteBufAll ...

  9. Netty源码分析第5章(ByteBuf)---->第5节: directArena分配缓冲区概述

    Netty源码分析第五章: ByteBuf 第五节: directArena分配缓冲区概述 上一小节简单分析了PooledByteBufAllocator中, 线程局部缓存和arean的相关逻辑, 这 ...

随机推荐

  1. performSelector 多参调用的实现方案

    1.nsinvocation封装: 2.msg_send封装: 第二种方案是系统的原生封装.

  2. hdu2824 The Euler function(欧拉函数个数)

    版权声明:本文为博主原创文章,未经博主同意不得转载. vasttian https://blog.csdn.net/u012860063/article/details/36426357 题目链接:h ...

  3. 1834. [ZJOI2010]网络扩容【费用流】

    Description 给定一张有向图,每条边都有一个容量C和一个扩容费用W.这里扩容费用是指将容量扩大1所需的费用. 求:  1.在不扩容的情况下,1到N的最大流:  2.将1到N的最大流增加K所需 ...

  4. 文件上传 python

    def upload(): r = requests.post( url='http://upload.renren.com/upload.fcgi?pagetype=addpublishersing ...

  5. $NOIp$前的日常

    嗯,一想到没准今年\(NOIp\)之后就要退役了,觉得没准这篇博客就是我OI史上的绝唱了-- \(hhh\),希望不会这样. \(12.4\) 退役了是真的233-- 居然感到一身轻松啊qwqwq \ ...

  6. 【ps】Photoshop

    Photoshop Cs6  存在百度云上 另外,想用切图插件cutterman,则必须安装相匹配的ps版本

  7. 安装github教程

    1.注意事项 .在FF和chrome会把GitHub.application当成一个应用程序下载下来,安装下载下来的程序也会失败的,只能通过ie直接安装. 2.要用https,不能用http. 2.安 ...

  8. 阿里云linux服务器打开端口号

    之前linux回滚了下,然后就连不上xshell和filezille了,后台安全配置哪里也都打开了端口号了,还是不行.然后我就想重启下ssh服务 ,执行service sshd restart 提示1 ...

  9. php生成带自定义logo和带二维码跳转自定义地址的二维码

    index.php<?phpheader('Content-type:text/html;charset=UTF-8');// 指定允许其他域名访问header('Access-Control- ...

  10. rails使用Kindeditor网页编辑器

    在gemfile中加入(后面版本别丢) gem 'rails_kindeditor', '~> 0.5.0' $ bundle 创建配置文件,并且引入js rails g rails_kinde ...