sentinel集群节点ClusterNode
Sentinel整体架构如下
图里从下往上可以看到,核心的部分包含 规则(rules)
、 处理插槽(slot)
、 调用链路(invocation tree)
、 集群节点(cluster node)
、 滑动窗口(slading winodw)
5部分。
// todo
(4) 源码解读
(4.1) 节点统计信息插槽-ClusterBuilderSlot
ClusterBuilderSlot
负责维护资源运行统计信息(响应时间、qps、线程数、异常),以及调用者列表
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);
}
}