sentinel集群节点ClusterNode

Sentinel整体架构如下
Sentinel架构
图里从下往上可以看到,核心的部分包含 规则(rules)处理插槽(slot)调用链路(invocation tree)集群节点(cluster node)滑动窗口(slading winodw) 5部分。

// todo

(4) 源码解读

(4.1) 节点统计信息插槽-ClusterBuilderSlot

ClusterBuilderSlot负责维护资源运行统计信息(响应时间、qps、线程数、异常),以及调用者列表

https://github.com/alibaba/Sentinel/blob/1.8.6/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java#L77

package com.alibaba.csp.sentinel.slots.clusterbuilder;
 
/**
 * 该槽维护资源运行统计信息(响应时间、qps、线程数、异常),以及调用者列表,由 ContextUtil#enter(String origin)标记 
 * 一个资源只有一个集群节点,而一个资源可以有多个默认节点。
 */
@Spi(isSingleton = false, order = Constants.ORDER_CLUSTER_BUILDER_SLOT)
public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> {

    /**
     * 相同的资源 ResourceWrapper#equals(Object) 将在全局范围内共享相同的ProcessorSlotChain,无论在哪个上下文中。
     * 因此,如果代码进入 entry(),资源名称必须相同,但上下文名称可能不同。
     * 
     * 为了获取同一资源在不同上下文中的总统计信息,同一资源在全局共享相同的ClusterNode。所有ClusterNode都缓存在此映射中。
     * 
     * 应用程序运行的时间越长,这个映射就会变得越稳定。所以我们不是并发映射而是锁。因为这个锁只发生在最开始,而并发映射将一直持有锁。
     * 
     */
    private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap = new HashMap<>();

    private static final Object lock = new Object();

    private volatile ClusterNode clusterNode = null;

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args)
        throws Throwable {
        // 单例-DCL
        if (clusterNode == null) {
            synchronized (lock) {
                if (clusterNode == null) {
                    // 创建集群节点​​。
                    clusterNode = new ClusterNode(resourceWrapper.getName(), resourceWrapper.getResourceType());
                    HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<>(Math.max(clusterNodeMap.size(), 16));
                    newMap.putAll(clusterNodeMap);
                    // 添加到缓存里
                    newMap.put(node.getId(), clusterNode);
                    // 更新缓存
                    clusterNodeMap = newMap;
                }
            }
        }
        node.setClusterNode(clusterNode);

        /*
         * 如果设置了原始上下文,我们应该获取 或 创建原始直接的Node。
         */
        if (!"".equals(context.getOrigin())) {
            Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
            context.getCurEntry().setOriginNode(originNode);
        }

        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }

}