在一个资源的 ProcessorSlotChain 中,NodeSelectorSlot 负责为资源创建 DefaultNode,这个 DefaultNode 仅限同名的 Context 使用。所以一个资源可能会存在多个 DefaultNode,那么想要获取一个资源的总的 QPS 就必须要遍历这些 DefaultNode。为了性能考虑,Sentinel 会为每个资源创建一个全局唯一的 ClusterNode,用于统计资源的全局并行占用线程数、QPS、异常总数等指标数据。
ClusterBuilderSlot
与 NodeSelectorSlot 的职责相似,ClusterBuilderSlot 的职责是为资源创建全局唯一的 ClusterNode,仅在资源第一次被访问时创建。ClusterBuilderSlot 还会将 ClusterNode 赋值给 DefaultNode.clusterNode,由 DefaultNode 持有 ClusterNode,负责管理 ClusterNode 的指标数据统计。这点也是 ClusterBuilderSlot 在 ProcessorSlotChain 链表中必须排在 NodeSelectorSlot 之后的原因,即必须先有 DefaultNode,才能将 ClusterNode 交给 DefaultNode 管理。
public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
//key: resource value:ClusterNode 每个资源全局对应一个ClusterNode
private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap = new HashMap<>();
private static final Object lock = new Object();
//每个资源全局对应一个chain,也就是全局对应一个ClusterBuilderSlot,全局对应一个ClusterNode
private volatile ClusterNode clusterNode = null;
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args)
throws Throwable {
if (clusterNode == null) {
synchronized (lock) {
if (clusterNode == null) {
// Create the cluster node.
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;
}
}
}
//DefaultNode中持有ClusterNode的对象
node.setClusterNode(clusterNode);
if (!"".equals(context.getOrigin())) {
//在这里根据来源创建StatisticNode并关联
Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
//将origin对应的StatisticNode赋值到当前的CtEntry中,避免后续每次从map中取
context.getCurEntry().setOriginNode(originNode);
}
//击鼓传花,向后传递
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
//什么也没有做,向后传递
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
public static ClusterNode getClusterNode(String id, EntryType type) {
return clusterNodeMap.get(new StringResourceWrapper(id, type));
}
}