io.netty.channel.DefaultChannelPipeline implements ChannelPipleline
 

DefaultChannelPiple给出了ChannelPipleline的默认实现。ChannelPipleline是一个双向链表,本章的内容是分析默认实现中双向链表的实现。

双向列表的的数据结构

  DefaultChannelPiple使用了三种节点类型: HeadContext, TailContext, DefaultChannelHandlerContext,这三中类型都是派生自AbstractChannelHandlerContext,这个抽象类中有双向链表所需要的两个关键属性next和prev。链表的初始化代码在构造方法中。

 protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel"); 4 tail = new TailContext(this);
5 head = new HeadContext(this);
6
7 head.next = tail;
8 tail.prev = head;
}

  构造方法的第4-8行,时候是链表的初始化代码。HeadContext是链表头的类型,TailContext是链表尾的类型,这两个类型是DefaultChannelPiple的内部类。链表的头和尾节点是不持有channelHandler的,相比于中间节点,这两个节点比较特殊。有专门的方法用来创建中间节点,如下所示:

 private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}

 

添加channelHandler

  向链表中添加channelHandler的方法有两大类型:

  1. addFirst,addLast
  2. addAfter,addBefore

  在ChannelPiple中,每一个handler是有名字的,如果用户没有给handler命名,在添加过程中会为它生成一个不重复的名字。如果用户给handler命名重复,添加handler将会失败同时抛出异常。两种类型的添加方法最大的不同之处在于,第1中会把新节点添加在head之后或tail之前。第2种必须先要首先找到指定名字的节点,然后把新节点添加到这个节点之后或之前。如果没找到指定名字的节点也会导致添加失败同时抛出异常。下面以addAfter为例分析添加过程。

 public final ChannelPipeline addAfter(
EventExecutorGroup group, String baseName, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
final AbstractChannelHandlerContext ctx; synchronized (this) {
checkMultiplicity(handler);
8 name = filterName(name, handler);
9 ctx = getContextOrDie(baseName);
10
11 newCtx = newContext(group, name, handler);
12
13 addAfter0(ctx, newCtx); EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
21 callHandlerAdded0(newCtx);
}
});
return this;
}
}
27 callHandlerAdded0(newCtx);
return this;
}

  第8行,filterName方法,确保handler有一个名字,如果name==null, 生成一个不重复的名字。然后检查是否有重名的,如果用户指定名字重复抛出异常。

  第9行,找到baseName对应的节点,如果没有抛出异常。

  第11行, 创建新的节点,这个节点将持有hanler,同时给这个节点分配一个eventExecutor。

  第13行,添加链表节点的操作。

  第21,27行,调用handler的handlerAdded方法,如果捕捉到异常,从链表中删除这个刚刚添加的节点,然后调用handler的handlerRemoved方法, 调用fireExceptionCaught方法触发异常事件。

  其它几个添加方法几个add方法和addAfter大致相同。addBefore是把addAfter0变成了addBefore0。addFirst中没有getContextOrDie调用,把addAfter0替换陈addFirst0。addLast在addFirst的基础上把addFirst0替换成addLast0。

  名字是维护链表节点的一个重要因素,DefaultChannelPipleline需要确保链表中的每个节点的名字都重复,这样它才能通过名字找到一个唯一的节点。用户添加一个handler时,如果由于用户命名不当导致的名字重复,这个handler将会被拒绝添加的链表中。如果用户以匿名方式添加handler,添加之前DefaultChannelPipleline会为这个handler生成一个不重复的名字,这个功能在filterName方法中实现。

 private String filterName(String name, ChannelHandler handler) {
if (name == null) {
return generateName(handler);
}
checkDuplicateName(name);
return name;
}

  generateName方法负责为匿名的handler生成一个名字,checkDuplicateName负责验证用户提供的名字是否重复。名字的生成规则是handler的类型名+"#n",假设你的handler的类型名是com.test.YourClass, 那么生成名字将是YourClass#0, YourClass#1, ..., YourClass#n。

删除链表节点

  所有的remove方法最终都会调用到private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx)方法.

 private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
assert ctx != head && ctx != tail; synchronized (this) {
5 remove0(ctx); EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@Override
public void run() {
12 callHandlerRemoved0(ctx);
}
});
return ctx;
}
}
18 callHandlerRemoved0(ctx);
return ctx;
}

  5行,从链表结构中删除handler。

  12,18行, 调用handler的handlerRemoved方法。

链表节点查找

  查找方法get最终都会调用内部的context0方法,这个方法是纯粹的链表操作,比较单纯。

替换链表节点

  所有的replace方法最终都会调用内部的replace方法:

  private ChannelHandler replace(final AbstractChannelHandlerContext ctx, final String newName, ChannelHandler newHandler)

  这个方法代码结构与addAfter相似,不同的是在链表操作上是一个替换操作,之后会先调用被替换handler的handlerRemoved方法,然后调用新handler的handlerAdded方法。

链表操作会handler方法之间的调用关系

链表方法 ChannelHandler方法
addBefore,addAfter,addFirst,addLast handlerAdded  
get 无
remove,removeFirst,removeLast handleRemoved
replace handleRemoved, handlerAdded

  

netty源码解解析(4.0)-9 ChannelPipleline的默认实现-链表管理的更多相关文章

  1. netty源码解解析(4.0)-10 ChannelPipleline的默认实现--事件传递及处理

    事件触发.传递.处理是DefaultChannelPipleline实现的另一个核心能力.在前面在章节中粗略地讲过了事件的处理流程,本章将会详细地分析其中的所有关键细节.这些关键点包括: 事件触发接口 ...

  2. netty源码解解析(4.0)-11 Channel NIO实现-概览

      结构设计 Channel的NIO实现位于io.netty.channel.nio包和io.netty.channel.socket.nio包中,其中io.netty.channel.nio是抽象实 ...

  3. netty源码解解析(4.0)-17 ChannelHandler: IdleStateHandler实现

    io.netty.handler.timeout.IdleStateHandler功能是监测Channel上read, write或者这两者的空闲状态.当Channel超过了指定的空闲时间时,这个Ha ...

  4. netty源码解解析(4.0)-18 ChannelHandler: codec--编解码框架

    编解码框架和一些常用的实现位于io.netty.handler.codec包中. 编解码框架包含两部分:Byte流和特定类型数据之间的编解码,也叫序列化和反序列化.不类型数据之间的转换. 下图是编解码 ...

  5. netty源码解解析(4.0)-20 ChannelHandler: 自己实现一个自定义协议的服务器和客户端

    本章不会直接分析Netty源码,而是通过使用Netty的能力实现一个自定义协议的服务器和客户端.通过这样的实践,可以更深刻地理解Netty的相关代码,同时可以了解,在设计实现自定义协议的过程中需要解决 ...

  6. netty源码解解析(4.0)-15 Channel NIO实现:写数据

    写数据是NIO Channel实现的另一个比较复杂的功能.每一个channel都有一个outboundBuffer,这是一个输出缓冲区.当调用channel的write方法写数据时,这个数据被一系列C ...

  7. netty源码解解析(4.0)-8 ChannelPipeline的设计

    io.netty.channel.ChannelPipeline   设计原理 上图中,为了更直观地展示事件处理顺序, 故意有规律地放置两种handler的顺序,实际上ChannelInboundHa ...

  8. netty源码解解析(4.0)-14 Channel NIO实现:读取数据

     本章分析Nio Channel的数据读取功能的实现. Channel读取数据需要Channel和ChannelHandler配合使用,netty设计数据读取功能包括三个要素:Channel, Eve ...

  9. netty源码解解析(4.0)-4 线程模型-概览

    netty线程体系概览 netty的高并发能力很大程度上由它的线程模型决定的,netty定义了两种类型的线程: I/O线程: EventLoop, EventLoopGroup.一个EventLoop ...

随机推荐

  1. python 常用知识点

    1,字典get用法 如果key没有值,返回一个None >>> dic = {'k1':'v1','k2':'v2','k3':'v3'} >>> dic.get( ...

  2. 75.iOS内存管理

    堆区和栈区 1.栈区:由编译器自动分配释放,函数的参数值,局部变量等值 2.堆区:一般由开发人员分配释放,若不释放,则可能会引起内存泄漏 NSString *string = @"abcd& ...

  3. Unity3D连接本地或局域网MySQL数据库

    准备工作: 1.打开 Unity3D 安装目录,到这个路径下 Editor > Data > Mono > lib > mono > 2.0 拷贝出下图的五个动态链接库, ...

  4. C# 监听HTTP请求

    先把代码放在这里,下面再详细解说: using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Oracle.DataAccess.Client; ...

  5. web开发之菜鸟的代码规范

    笔者菜鸟里最不会飞的那个,所以这些基础的习惯都没养成,正好抽时间特意做个笔记以方便自己规范代码, 有兴趣的大佬多多指点. 养成好的编码习惯收益良多, 总结下编码时应注意的细节<借鉴高程里代码约束 ...

  6. Java学习笔记35(异常)

    代码在运行中发生的问题就是异常 java中把多种异常封装多个类,当程序出现问题时候,就会创建异常类对象并且抛出相关信息 异常体系: Throwable类是Java中所有错误或异常的父类 Throwab ...

  7. LabVIEW(十六):多列列表框控件

    1.多列列表框控件:前面板右键>列表.表格和树>多列列表框2.默认情况下只显示列首,可设置显示行首:前面板选中多列列表框右键>显示>行首3.LabVIEW中提供42种自带的图标 ...

  8. Python模块学习 - fabric

    简介 fabric是一个Python的库,同时它也是一个命令行工具.使用fabric提供的命令行工具,可以很方便地执行应用部署和系统管理等操作. fabric依赖于paramiko进行ssh交互,fa ...

  9. LeetCode: 150_Evaluate Reverse Polish Notation | 分析逆波兰式 | Medium

    题目: Evaluate Reverse Polish Notation Evaluatethe value of an arithmetic expression in Reverse Polish ...

  10. Python又把GUI界面攻下了,今天就告诉你怎么玩

    0.引言 学Python这么久了,一直想做个界面出来,最近发现Python有个内置库tkinter,利用它可以很轻松做出一些简易的UI界面,首先来看Python官方对Tkinter的说明: The t ...