核心链路调用
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
//开启链路调用
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
资源调用会创建一个CtEntry,然后开始执行上面创建的执行链
NodeSelectorSlot
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
//一个context+resource确定一个DefaultNode
DefaultNode node = map.get(context.getName());
//双重检查
if (node == null) {
synchronized (this) {
//运行上下对应一个DefaultNode
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);
}
}
context.setCurNode(node);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
这个slot主要是为了收集资源路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级。首先判断缓存map中是否已经生成了DefaultNode,如果没有则进入双重检查逻辑,创建DefaultNode并设置资源,一个context+resource确定一个DefaultNode,构建请求的资源路径,构建树状结构,并且设置context中当前调用的node。这个类上有详细的注解解释我们要仔细看一下
ContextUtil.enter("entrance1", "appA");
Entry nodeA = SphU.entry("nodeA");
if (nodeA != null) {
nodeA.exit();
}
ContextUtil.exit();
上述代码通过 ContextUtil.enter() 创建了一个名为 entrance1 的上下文,同时指定调用发起者为 appA;接着通过 SphU.entry()判断请求是否可以正常通过,如果不能顺利通过发生限流降级则会抛出 BlockException。
以上代码将在内存中生成以下结构:
machine-root
* /
* /
* EntranceNode1
* /
* /
* DefaultNode(nodeA)- - - - - -> ClusterNode(nodeA);
每个 DefaultNode 由资源 ID 和输入名称来标识,同一个资源在不同的context中会有不同的DefaultNode,比如
ContextUtil.enter("entrance1", "appA");
Entry nodeA = SphU.entry("nodeA");
if (nodeA != null) {
nodeA.exit();
}
ContextUtil.exit();
ContextUtil.enter("entrance2", "appA");
nodeA = SphU.entry("nodeA");
if (nodeA != null) {
nodeA.exit();
}
ContextUtil.exit();
以上代码将在内存中生成以下结构:
machine-root
* / \
* / \
* EntranceNode1 EntranceNode2
* / \
* / \
* DefaultNode(nodeA) DefaultNode(nodeA)
* | |
* +- - - - - - - - - - +- - - - - - -> ClusterNode(nodeA);
可以看到同一个资源在不同context中会有不同的DefaultNode,但是全局只有有一个ClusterNode
ClusterBuilderSlot
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 {
if (clusterNode == null) {
synchronized (lock) {
//双重检查
if (clusterNode == null) {
// Create the cluster node.
//创建clusterNode,用于统计全局请求指标
clusterNode = new ClusterNode();
//这里为什么不使用ConcurrentHashMap
HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<>(Math.max(clusterNodeMap.size(), 16));
newMap.putAll(clusterNodeMap);
newMap.put(node.getId(), clusterNode);
clusterNodeMap = newMap

本文围绕Java Sentinel核心链路调用展开分析。资源调用创建CtEntry后执行执行链,涉及NodeSelectorSlot、ClusterBuilderSlot等多个Slot。各Slot功能不同,如NodeSelectorSlot收集资源路径,ClusterBuilderSlot构建资源节点,FlowSlot进行流控,DegradeSlot进行降级判断等,最后完成实时数据统计。
最低0.47元/天 解锁文章
1601

被折叠的 条评论
为什么被折叠?



