【sentinel】深入浅出之原理篇NodeSelectorSlot

这篇文章开始分析SlotChain中的各个Slot,第一个Slot为NodeSelectorSlot
NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;

public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {
   private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);

   @Override
   public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
       throws Throwable {
       //从上下文获取Context命名的Node节点
       DefaultNode node = map.get(context.getName());
       if (node == null) {
           synchronized (this) {
               node = map.get(context.getName());
               if (node == null) {
                   //创建resource对应的Node 类型为DeaultNode
                   node = Env.nodeBuilder.buildTreeNode(resourceWrapper, null);
                   //保存Node  一个resource对应一个Node
                   HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
                   cacheMap.putAll(map);
                   cacheMap.put(context.getName(), node);
                   map = cacheMap;
               }
               // 添加到Context的Node节点,这里构造了一棵节点🌲
               ((DefaultNode)context.getLastNode()).addChild(node);
           }
       }
       //设置当前Context的当前节点为node
       context.setCurNode(node);
       //调用下一个Slot
       fireEntry(context, resourceWrapper, node, count, prioritized, args);
   }

   @Override
   public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
       fireExit(context, resourceWrapper, count, args);
   }
}

可以看到,在NodeSelectorSlot节点中,首先根据ContextName获取默认节点,若默认节点不存在,则创建一个默认节点,并将改节点保存在缓存map中,然后设置当前节点为默认节点

调用顺序资源的调用路径树形结构哪里存储?

重点就在于下面这行代码

 ((DefaultNode)context.getLastNode()).addChild(node);

通过上下文首先获取最后一个节点,如果当前节点不为null,则当前节点就为最后一个节点,如果当前节点为null,则为entranceNode。
获取到最后一个节点,然后将当前节点挂在最后一个节点后面。

public class Context {
    private final String name;
    private DefaultNode entranceNode;
    private Entry curEntry;
    private String origin = "";
    private final boolean async;

    public Node getLastNode() {
        //从当前节点获取
        if (curEntry != null && curEntry.getLastNode() != null) {
            return curEntry.getLastNode();
        } else {
            //直接返回entranceNode
            return entranceNode;
        }
    }
}
public class DefaultNode extends StatisticNode {

    private ResourceWrapper id;
    private volatile Set<Node> childList = new HashSet<>();
    private ClusterNode clusterNode;

    public void addChild(Node node) {
        if (node == null) {
            RecordLog.warn("Trying to add null child to node <{0}>, ignored", id.getName());
            return;
        }
        //如果子节点包含了该节点,则跳过
        if (!childList.contains(node)) {
            synchronized (this) {
                //不包含该节点,则将当前节点挂在子节点里面
                if (!childList.contains(node)) {
                    Set<Node> newSet = new HashSet<>(childList.size() + 1);
                    newSet.addAll(childList);
                    newSet.add(node);
                    childList = newSet;
                }
            }
            RecordLog.info("Add child <{0}> to node <{1}>", ((DefaultNode)node).id.getName(), id.getName());
        }
    }
}

这种结构有点类似大学学的数据结构:
先来的位于栈首,后来的位于栈尾,后进先出。栈首位置永远是EntranceNode节点
举个例子:创建一个Context和5个resourceEntry,看一下整个树的结构会是什么样子。

 public static void main(String[] args) {
    try {
        Context context=ContextUtil.enter("context1");
        Entry entry=SphU.entry("A");
        Entry entry2=SphU.entry("B");
        Entry entry3=SphU.entry("C");
        Entry entry4=SphU.entry("D");
        Entry entry5=SphU.entry(""E);
        entry.exit();
        entry2.exit();
        entry3.exit();
        entry4.exit();
        entry5.exit();
        ContextUtil.exit();
    } catch (BlockException ex) {
        // 处理被流控的逻辑
        System.out.println("blocked!");
    }catch (Exception e){
        e.printStackTrace();
    }
}

运行结果如下:


运行结果.png

可以看到,EntranceNode的子节点为A,A的子节点为B,B的子节点为C,一直到E。

既然子Node都是一个,那么为什么还需要HashSet来保存,而不是定义一个DefaultNode?

public static void main(String[] args) {

    try {
        Context context=ContextUtil.enter("context1");
        Entry entry=SphU.entry("A");
        Entry entry2=SphU.entry("B");
        entry2.exit();
        Entry entry3=SphU.entry("C");
        entry3.exit();
        Entry entry4=SphU.entry("D");
        Entry entry5=SphU.entry("E");
        entry.exit();
/*                entry2.exit();
        entry3.exit();*/
        entry4.exit();
        entry5.exit();
        ContextUtil.exit();
    } catch (BlockException ex) {
        // 处理被流控的逻辑
        System.out.println("blocked!");
    }catch (Exception e){
        e.printStackTrace();
    }
}

运行上述代码,你就会发现,节点A的子Node有三个,B,C,D,节点D的子节点有一个E,如图:


运行结果

为什么会是这样?

上面提到,所有树的结构类似于栈,后入先出,当最后一个节点exit之后,最后一个节点的前一个节点为最后一个节点,就会存在多个子节点的问题。

再通过代码可以看到,一个 ContextName对应同一个 Resource使用同一个DefaultNode,一个 ContextName对应不同Resource使用同一个EntranceNode

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,498评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,668评论 3 406
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,857评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,305评论 1 303
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,308评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,747评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,078评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,080评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,649评论 1 327
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,644评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,760评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,352评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,076评论 3 341
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,490评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,651评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,353评论 3 383
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,828评论 2 367