sentinel中的NodeSelectorSlot执行流程

在上一篇文章(sentinel调用上下文(Context)数据结构分析)中,我们分析出sentinel通过函数Entry e = new CtEntry(resourceWrapper, chain, context);

创建好了context,context的结构如下:

当每次调用,SphU.entry("abc")时,都会创建一个新的Entry对象时,都会重新设置context的curEntry,并将context原来的curEntry设置为该新Entry对象的父节点。这样每个资源都是链表中的一个节点。但是这个时候链表中的节点只有树没有叶子节点。那么叶子节点在哪里添加呢?就在NodeSelectorSlot中。NodeSelectorSlot的代码如下:

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, Object... args) throws Throwable {
    // 根据「上下文」的名称获取DefaultNode
    // 多线程环境下,每个线程都会创建一个context,
    // 只要资源名相同,则context的名称也相同,那么获取到的节点就相同
    DefaultNode node = map.get(context.getName());
    if (node == null) {
        synchronized (this) {
            node = map.get(context.getName());
            if (node == null) {
                // 如果当前「上下文」中没有该节点,则创建一个DefaultNode节点
                node = Env.nodeBuilder.buildTreeNode(resourceWrapper, null);
                // 省略部分代码
            }
            // 将当前node作为「上下文」的最后一个节点的子节点添加进去
            // 如果context的curEntry.parent.curNode为null,则添加到entranceNode中去
            // 否则添加到context的curEntry.parent.curNode中去
            ((DefaultNode)context.getLastNode()).addChild(node);
        }
    }
    // 将该节点设置为「上下文」中的当前节点
    // 实际是将当前节点赋值给context中curEntry的curNode
    // 在Context的getLastNode中会用到在此处设置的curNode
    context.setCurNode(node);
    fireEntry(context, resourceWrapper, node, count, args);
}

上面的代码可以分解成下面这些步骤:
1)获取当前上下文对应的DefaultNode,如果没有的话会为当前的调用新生成一个DefaultNode节点,它的作用是对资源进行各种统计度量以便进行流控;
2)将新创建的DefaultNode节点,添加到context中,作为「entranceNode」或者「curEntry.parent.curNode」的子节点;
3)将DefaultNode节点,添加到context中,作为「curEntry」的curNode。

在创建好node后,将它设置为context的当前节点

context.setCurNode(node);

public Context setCurNode(Node node) {
        this.curEntry.setCurNode(node);
        return this;
    }

这样树节点curEntry就有叶子了,结构如下:

依次执行每个资源的NodeSelectorSlot节点,就变成如下结构:这样每个树节点就有叶子节点了。

叶子节点的保存

         在上面的代码中,创建好了DefaultNode node,除了需要设置

context.setCurNode(node);

将node设置为context的当前节点,还需要

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

即将这个节点添加到lastNode中,getLastNode()的代码如下:

public Node getLastNode() {
        if (curEntry != null && curEntry.getLastNode() != null) {
            return curEntry.getLastNode();
        } else {
            return entranceNode;
        }
    }


public Node getLastNode() {
        return parent == null ? null : parent.getCurNode();
    }

如果context中curEntry不为空并且curEntry.getLastNode()不为空,返回curEntry.getLastNode()。

而curEntry.getLastNode()就是返回curEntry的父节点的curNode。

所以当第一次执行时,会返回entranceNode。这样context的结构如下:

如果在一个context中请求多个资源,第二次执行NodeSelectorSlot,这个时候curEntry.getLastNode()不为空,这个时候context就变成下面这样

然后将当前node设置给context的curNode,上面的图形就变成下图这样:

假如再创建一个Entry,然后再进入一次不同的资源名,上面的图就变成下面这样:

至此NodeSelectorSlot的基本功能已经大致分析清楚了。

PS:以上的分析是基于每次执行SphU.entry(name)时,资源名都是不一样的前提下。如果资源名都一样的话,那么生成的node都相同,则只会再第一次把node加入到entranceNode的子节点中去,其他的时候,只会创建一个新的Entry,然后替换context中的curEntry的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值