高级篇-sentinel限流slot责任链设计(一)

高级篇-sentinel限流slot责任链设计(一)

前言

在这之前,发的文章都是一些简单的,入门级别的介绍,涉及源码级别的介绍并不多,本文开始,就会逐渐的开始介入源码的讲解。

本文会从sentinel的核心知识点 ,slot机制来讲解。

源码入口

下面的代码是调用SphU.entry("HelloWorld") 之后进去的核心方法,整个的限流操作都是在这里面完成。

源码入口: com.alibaba.csp.sentinel.CtSph#entryWithPriority

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
        throws BlockException {
        //省略大部分代码。。。。
        // 构建slot链,这个地方就是构建责任链的地方
        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper); // 代码1
        if (chain == null) {
            return new CtEntry(resourceWrapper, null, context);
        }
        Entry e = new CtEntry(resourceWrapper, chain, context);
        try {
            // 开始执行责任链
            chain.entry(context, resourceWrapper, null, count, prioritized, args); // 代码2
        } 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;
    }

上面的代码主要分为两步,

代码1: 构建责任链

ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
        // 根据资源获取slot功能链
        ProcessorSlotChain chain = chainMap.get(resourceWrapper);
        if (chain == null) {
            // 上锁保证仅会初始化一个
            synchronized (LOCK) {
                chain = chainMap.get(resourceWrapper);
                if (chain == null) {
                    // Entry size limit. 最大的资源大小是6000个
                    if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
                        return null;
                    }
                    //构建一个slot引用链 ----- 
                    chain = SlotChainProvider.newSlotChain();
                    // map存储
                    Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
                        chainMap.size() + 1);
                    newMap.putAll(chainMap);
                    newMap.put(resourceWrapper, chain);
                    chainMap = newMap;
                }
            }
        }
        return chain;
    }

上面的代码简单来说,就是从chainMap里面获取slot功能链, 没有的话,就构建一个,这里需要注意一点Constants.MAX_SLOT_CHAIN_SIZE , chainMap是限制了大小,最大不能超过6000, 也就是说,默认不能超过6000个资源,如果超过6000个资源,则会有资源的限流没办法生效

SlotChainProvider.newSlotChain() 通过这个方法来进行构建的。

public static ProcessorSlotChain newSlotChain() {
       // 不为空,则直接返回
        if (slotChainBuilder != null) {
            return slotChainBuilder.build();
        }
    // 构建slot chain ,通过SPI机制来
    slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class);
    // SPI获取不到的话,则使用默认的
    if (slotChainBuilder == null) {
        // Should not go through here.
        RecordLog.warn(&quot;[SlotChainProvider] Wrong state when resolving slot chain builder, using default&quot;);
        slotChainBuilder = new DefaultSlotChainBuilder();
    } else {
        RecordLog.info(&quot;[SlotChainProvider] Global slot chain builder resolved: &quot;
            + slotChainBuilder.getClass().getCanonicalName());
    }
    // 返回结果
    return slotChainBuilder.build();
}

通过SPI机制来构建slot chain, com.alibaba.csp.sentinel.slotchain.SlotChainBuilder 具体加载的上面SlotChainBuilder 可以查看所有项目(META-IN/services)下的这个文件.

代码2:执行责任链

构建出来的slot链如下,执行顺序也是这样。

NodeSelectorSlot

负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级

ClusterBuilderSlot

则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;

此插槽用于构建资源的 ClusterNode 以及调用来源节点。ClusterNode 保持某个资源运行统计信息(响应时间、QPS、block 数目、线程数、异常数等)以及调用来源统计信息列表。调用来源的名称由 ContextUtil.enter(contextName,origin) 中的 origin 标记。可通过如下命令查看某个资源不同调用者的访问情况:curl http://localhost:8719/origin?id=caller

id: nodeA
idx origin  threadNum passedQps blockedQps totalQps aRt   1m-passed 1m-blocked 1m-total 
1   caller1 0         0         0          0        0     0         0          0        
2   caller2 0         0         0          0        0     0         0          0
LogSlot

用于输出拦截日志“sentinel-block.log”

StatisticSlot:

StatisticSlot 是 Sentinel 的核心功能插槽之一,用于统计实时的调用数据。

  • clusterNode:资源唯一标识的 ClusterNode 的实时统计
  • origin:根据来自不同调用者的统计信息
  • defaultnode: 根据入口上下文区分的资源 ID 的 runtime 统计
  • 入口流量的统计

Sentinel 底层采用高性能的滑动窗口数据结构 LeapArray 来统计实时的秒级指标数据,可以很好地支撑写多于读的高并发场景。

FlowSlot

这个 slot 主要根据预设的资源的统计信息,按照固定的次序,依次生效。如果一个资源对应两条或者多条流控规则,则会根据如下次序依次检验,直到全部通过或者有一个规则生效为止:

  • 指定应用生效的规则,即针对调用方限流的;

  • 调用方为 other 的规则;

  • 调用方为 default 的规则。

AuthoritySlot

则根据配置的黑白名单和调用来源信息,来做黑白名单控制;

DegradeSlot:

这个 slot 主要针对资源的平均响应时间(RT)以及异常比率,来决定资源是否在接下来的时间被自动熔断掉。

SystemSlot

这个 slot 会根据对于当前系统的整体情况,对入口资源的调用进行动态调配。其原理是让入口的流量和当前系统的预计容量达到一个动态平衡。

注意系统规则只对入口流量起作用(调用类型为 EntryType.IN),对出口流量无效。可通过 SphU.entry(res, entryType) 指定调用类型,如果不指定,默认是EntryType.OUT

总体的框架如下:

Sentinel 将 ProcessorSlot 作为 SPI 接口进行扩展(1.7.2 版本以前 SlotChainBuilder 作为 SPI),使得 Slot Chain 具备了扩展的能力。您可以自行加入自定义的 slot 并编排 slot 间的顺序,从而可以给 Sentinel 添加自定义的功能。

slot的设计是sentinel的核心设计,采用责任链的模式,通过一个一个的功能卡槽去进行去完成对应的,这种设计有好处也有坏处。

优点:就是可以非常简单的加入新的功能,扩展性非常强

缺点:每个功能槽的功能很独立,统计数据是一个,判断规则是一个, 这样会导致统计数据和使用数据分离,线程安全是个问题,会导致实际的限流会有一定程度的不准确

sharedCode源码交流群,欢迎喜欢阅读源码的朋友加群,添加下面的微信, 备注”加群“ 。

前言

在这之前,发的文章都是一些简单的,入门级别的介绍,涉及源码级别的介绍并不多,本文开始,就会逐渐的开始介入源码的讲解。

本文会从sentinel的核心知识点 ,slot机制来讲解。

源码入口

下面的代码是调用SphU.entry(“HelloWorld”) 之后进去的核心方法,整个的限流操作都是在这里面完成。

源码入口: com.alibaba.csp.sentinel.CtSph#entryWithPriority

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
        throws BlockException {
        //省略大部分代码。。。。
    	// 构建slot链,这个地方就是构建责任链的地方
        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper); // 代码1
        if (chain == null) {
            return new CtEntry(resourceWrapper, null, context);
        }
        Entry e = new CtEntry(resourceWrapper, chain, context);
        try {
            // 开始执行责任链
            chain.entry(context, resourceWrapper, null, count, prioritized, args); // 代码2
        } 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;
    }

上面的代码主要分为两步,

代码1: 构建责任链

ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
    	// 根据资源获取slot功能链
        ProcessorSlotChain chain = chainMap.get(resourceWrapper);
        if (chain == null) {
            // 上锁保证仅会初始化一个
            synchronized (LOCK) {
                chain = chainMap.get(resourceWrapper);
                if (chain == null) {
                    // Entry size limit. 最大的资源大小是6000个
                    if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
                        return null;
                    }
					//构建一个slot引用链 ----- 
                    chain = SlotChainProvider.newSlotChain();
                    // map存储
                    Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
                        chainMap.size() + 1);
                    newMap.putAll(chainMap);
                    newMap.put(resourceWrapper, chain);
                    chainMap = newMap;
                }
            }
        }
        return chain;
    }

上面的代码简单来说,就是从chainMap里面获取slot功能链, 没有的话,就构建一个,这里需要注意一点Constants.MAX_SLOT_CHAIN_SIZE , chainMap是限制了大小,最大不能超过6000, 也就是说,默认不能超过6000个资源,如果超过6000个资源,则会有资源的限流没办法生效

SlotChainProvider.newSlotChain() 通过这个方法来进行构建的。

public static ProcessorSlotChain newSlotChain() {
       // 不为空,则直接返回
        if (slotChainBuilder != null) {
            return slotChainBuilder.build();
        }

        // 构建slot chain ,通过SPI机制来
        slotChainBuilder = SpiLoader.loadFirstInstanceOrDefault(SlotChainBuilder.class, DefaultSlotChainBuilder.class);
		// SPI获取不到的话,则使用默认的
        if (slotChainBuilder == null) {
            // Should not go through here.
            RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default");
            slotChainBuilder = new DefaultSlotChainBuilder();
        } else {
            RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: "
                + slotChainBuilder.getClass().getCanonicalName());
        }
        // 返回结果
        return slotChainBuilder.build();
    }

通过SPI机制来构建slot chain, com.alibaba.csp.sentinel.slotchain.SlotChainBuilder 具体加载的上面SlotChainBuilder 可以查看所有项目(META-IN/services)下的这个文件.

代码2:执行责任链

构建出来的slot链如下,执行顺序也是这样。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhWMEvGs-1653975735080)(https://se-dd.com//shared/file/oneblog/20210408180701960.png)]

NodeSelectorSlot

负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级

ClusterBuilderSlot

则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;

此插槽用于构建资源的 ClusterNode 以及调用来源节点。ClusterNode 保持某个资源运行统计信息(响应时间、QPS、block 数目、线程数、异常数等)以及调用来源统计信息列表。调用来源的名称由 ContextUtil.enter(contextName,origin) 中的 origin 标记。可通过如下命令查看某个资源不同调用者的访问情况:curl http://localhost:8719/origin?id=caller

id: nodeA
idx origin  threadNum passedQps blockedQps totalQps aRt   1m-passed 1m-blocked 1m-total 
1   caller1 0         0         0          0        0     0         0          0        
2   caller2 0         0         0          0        0     0         0          0        
LogSlot

用于输出拦截日志“sentinel-block.log”

StatisticSlot:

StatisticSlot 是 Sentinel 的核心功能插槽之一,用于统计实时的调用数据。

  • clusterNode:资源唯一标识的 ClusterNode 的实时统计
  • origin:根据来自不同调用者的统计信息
  • defaultnode: 根据入口上下文区分的资源 ID 的 runtime 统计
  • 入口流量的统计

Sentinel 底层采用高性能的滑动窗口数据结构 LeapArray 来统计实时的秒级指标数据,可以很好地支撑写多于读的高并发场景。

FlowSlot

这个 slot 主要根据预设的资源的统计信息,按照固定的次序,依次生效。如果一个资源对应两条或者多条流控规则,则会根据如下次序依次检验,直到全部通过或者有一个规则生效为止:

  • 指定应用生效的规则,即针对调用方限流的;

  • 调用方为 other 的规则;

  • 调用方为 default 的规则。

AuthoritySlot

则根据配置的黑白名单和调用来源信息,来做黑白名单控制;

DegradeSlot:

这个 slot 主要针对资源的平均响应时间(RT)以及异常比率,来决定资源是否在接下来的时间被自动熔断掉。

SystemSlot

这个 slot 会根据对于当前系统的整体情况,对入口资源的调用进行动态调配。其原理是让入口的流量和当前系统的预计容量达到一个动态平衡。

注意系统规则只对入口流量起作用(调用类型为 EntryType.IN),对出口流量无效。可通过 SphU.entry(res, entryType) 指定调用类型,如果不指定,默认是EntryType.OUT

总体的框架如下:

Sentinel 将 ProcessorSlot 作为 SPI 接口进行扩展(1.7.2 版本以前 SlotChainBuilder 作为 SPI),使得 Slot Chain 具备了扩展的能力。您可以自行加入自定义的 slot 并编排 slot 间的顺序,从而可以给 Sentinel 添加自定义的功能。

slot的设计是sentinel的核心设计,采用责任链的模式,通过一个一个的功能卡槽去进行去完成对应的,这种设计有好处也有坏处。

优点:就是可以非常简单的加入新的功能,扩展性非常强

缺点:每个功能槽的功能很独立,统计数据是一个,判断规则是一个, 这样会导致统计数据和使用数据分离,线程安全是个问题,会导致实际的限流会有一定程度的不准确

sharedCode源码交流群,欢迎喜欢阅读源码的朋友加群,添加下面的微信, 备注”加群“ 。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值