sentinel源码分析-05核心链路调用

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

核心链路调用

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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值