概述

之前解析了插槽链的入口ProcessorSlotChain和它的默认实现DefaultProcessorSlotChain。具体的处理逻辑由链表中的各个节点处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
chain.addLast(new NodeSelectorSlot());
chain.addLast(new ClusterBuilderSlot());
chain.addLast(new LogSlot());
chain.addLast(new StatisticSlot());
chain.addLast(new SystemSlot());
chain.addLast(new AuthoritySlot());
chain.addLast(new FlowSlot());
chain.addLast(new DegradeSlot());

return chain;
}

本文解析以下链表中的第一个节点:NodeSelectorSlot

NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;

解析

NodeSelectorSlot

资源:在 Sentinel 里面,所有的资源都对应一个资源名称(resourceName),资源可以是一个类、一个方法、一个代码块。

继承关系

继承AbstractLinkedProcessorSlot,本身也是链表的一个节点

1
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object>{}

成员变量

使用 map 保存了资源信息。

1
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);

资源节点DefaultNode放在后面的小节

核心方法

entry

entry 进入资源区域时执行

  1. 根据context的名称获取资源node,如果没有获取到则新建一个更新到map中;
  2. 设置上下文中的 curNode;
  3. 交给下一个节点处理。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
    throws Throwable {
    /*
    * It's interesting that we use context name rather resource name as the map key.
    *
    * Remember that same resource({@link ResourceWrapper#equals(Object)}) will share
    * the same {@link ProcessorSlotChain} globally, no matter in which context. So if
    * code goes into {@link #entry(Context, ResourceWrapper, DefaultNode, int, Object...)},
    * the resource name must be same but context name may not.
    *
    * If we use {@link com.alibaba.csp.sentinel.SphU#entry(String resource)} to
    * enter same resource in different context, using context name as map key can
    * distinguish the same resource. In this case, multiple {@link DefaultNode}s will be created
    * of the same resource name, for every distinct context (different context name) each.
    *
    * Consider another question. One resource may have multiple {@link DefaultNode},
    * so what is the fastest way to get total statistics of the same resource?
    * The answer is all {@link DefaultNode}s with same resource name share one
    * {@link ClusterNode}. See {@link ClusterBuilderSlot} for detail.
    */
    DefaultNode node = map.get(context.getName());
    if (node == null) {
    synchronized (this) {
    node = map.get(context.getName());
    if (node == null) {
    node = new DefaultNode(resourceWrapper, null);
    HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
    cacheMap.putAll(map);
    cacheMap.put(context.getName(), node);
    map = cacheMap; // 采用的是替换
    }
    // Build invocation tree
    ((DefaultNode)context.getLastNode()).addChild(node);
    }
    }
    // 设置当前 curNode
    context.setCurNode(node);
    // 交给下一个节点处理
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }

exit

exit退出资源区域时执行.

NodeSelectorSlot 中不做任何处理,交由下一个节点处理。

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

DefaultNode

StatisticNode的解析放在单独一篇解析。

继承关系

1
public class DefaultNode extends StatisticNode{}

成员变量

1
2
3
4
5
private ResourceWrapper id; // 资源

private volatile Set<Node> childList = new HashSet<>(); // 子节点

private ClusterNode clusterNode; // 集群节点

构造器

1
2
3
4
public DefaultNode(ResourceWrapper id, ClusterNode clusterNode) {
this.id = id;
this.clusterNode = clusterNode;
}

主要方法

添加子节点 addChild

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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());
}
}

限流的QPS增加 increaseBlockQps

增加qps依赖父类的实现

1
2
3
4
5
@Override
public void increaseBlockQps(int count) {
super.increaseBlockQps(count);
this.clusterNode.increaseBlockQps(count);
}

异常的QPS增加

1
2
3
4
5
@Override
public void increaseExceptionQps(int count) {
super.increaseExceptionQps(count);
this.clusterNode.increaseExceptionQps(count);
}

响应时间统计

1
2
3
4
5
@Override
public void addRtAndSuccess(long rt, int successCount) {
super.addRtAndSuccess(rt, successCount);
this.clusterNode.addRtAndSuccess(rt, successCount);
}

线程数增加

1
2
3
4
5
 @Override
public void increaseThreadNum() {
super.increaseThreadNum();
this.clusterNode.increaseThreadNum();
}