zoukankan      html  css  js  c++  java
  • netty源码解析(4.0)-9 ChannelPipleline的默认实现-链表管理

    io.netty.channel.DefaultChannelPipeline implements ChannelPipleline
     

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

    双向列表的的数据结构

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

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

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

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

     

    添加channelHandler

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

    1. addFirst,addLast
    2. addAfter,addBefore

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

     1 public final ChannelPipeline addAfter(
     2             EventExecutorGroup group, String baseName, String name, ChannelHandler handler) {
     3         final AbstractChannelHandlerContext newCtx;
     4         final AbstractChannelHandlerContext ctx;
     5 
     6         synchronized (this) {
     7             checkMultiplicity(handler);
     8             name = filterName(name, handler);
     9             ctx = getContextOrDie(baseName);
    10 
    11             newCtx = newContext(group, name, handler);
    12 
    13             addAfter0(ctx, newCtx);
    14 
    15             EventExecutor executor = newCtx.executor();
    16             if (!executor.inEventLoop()) {
    17                 newCtx.setAddPending();
    18                 executor.execute(new Runnable() {
    19                     @Override
    20                     public void run() {
    21                         callHandlerAdded0(newCtx);
    22                     }
    23                 });
    24                 return this;
    25             }
    26         }
    27         callHandlerAdded0(newCtx);
    28         return this;
    29 }

      第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)方法.

     1 private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
     2         assert ctx != head && ctx != tail;
     3 
     4         synchronized (this) {
     5             remove0(ctx);
     6 
     7             EventExecutor executor = ctx.executor();
     8             if (!executor.inEventLoop()) {
     9                 executor.execute(new Runnable() {
    10                     @Override
    11                     public void run() {
    12                         callHandlerRemoved0(ctx);
    13                     }
    14                 });
    15                 return ctx;
    16             }
    17         }
    18         callHandlerRemoved0(ctx);
    19         return ctx;
    20 }

      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

     

      

     

     

     

  • 相关阅读:
    鼠标移入移出事件改变图片的分辨率
    Qt 5.2.0 和 VS 2012集成
    java int and string convert
    判断密码强度
    MySQL 警告WARN: Establishing SSL connection without server's identity verification is not recommended.解决办法
    java中byte转换int时为何与0xff进行与运算
    java排序练习
    小数的取舍
    控制台输入一个数组,然后倒序输出
    非托管资源的释放
  • 原文地址:https://www.cnblogs.com/brandonli/p/10137620.html
Copyright © 2011-2022 走看看