一、概述
在 Sentinel 里面,所有的资源都对应一个资源名称(resourceName
),每次资源调用都会创建一个 Entry
对象。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 SphU
API 显式创建。Entry 创建的时候,同时也会创建一系列功能插槽(slot chain),这些插槽有不同的职责,例如:
-
NodeSelectorSlot
负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级; -
ClusterBuilderSlot
则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据; -
LogSlot
则用于记录用于记录块异常,为故障排除提供具体的日志 -
StatisticSlot
则用于记录、统计不同纬度的 runtime 指标监控信息; -
FlowSlot
则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制; AuthoritySlot
则根据配置的黑白名单和调用来源信息,来做黑白名单控制;-
DegradeSlot
则通过统计信息以及预设的规则,来做熔断降级; SystemSlot
则通过系统的状态,例如 load1 等,来控制总的入口流量;
下面是关系结构图
二、AuthoritySlot分析
1.AuthoritySlot介绍
官方文档是这样描述AuthoritySlot的:
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
调用方信息通过
ContextUtil.enter(resourceName, origin)
方法中的origin
参数传入。规则配置
来源访问控制规则(
AuthorityRule
)非常简单,主要有以下配置项:
resource
:资源名,即限流规则的作用对象。limitApp
:对应的黑名单/白名单,不同 origin 用,
分隔,如appA,appB
。strategy
:限制模式,AUTHORITY_WHITE
为白名单模式,AUTHORITY_BLACK
为黑名单模式,默认为白名单模式。示例
比如我们希望控制对资源
test
的访问设置白名单,只有来源为appA
和appB
的请求才可通过,则可以配置如下白名单规则:AuthorityRule rule = new AuthorityRule(); rule.setResource("test"); rule.setStrategy(RuleConstant.AUTHORITY_WHITE); rule.setLimitApp("appA,appB"); AuthorityRuleManager.loadRules(Collections.singletonList(rule));
2.源码解读
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)throws Throwable {
checkBlackWhiteAuthority(resourceWrapper, context);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
1.在entry
阶段,执行了一个校验方法.
void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();
if (authorityRules == null) {
return;
}
Set<AuthorityRule> rules = authorityRules.get(resource.getName());//根据resource获取指定rules
if (rules == null) {
return;
}
for (AuthorityRule rule : rules) {
if (!AuthorityRuleChecker.passCheck(rule, context)) {
throw new AuthorityException(context.getOrigin(), rule);
}
}
}
2.如果AuthorityRuleManager
里面的authorityRules
为空,则跳过黑白名单的校验,否则根据resource
获取本资源的rules
(Set),循环这个rules
,在循环里面调用校验方法(AuthorityRuleChecker.passCheck(rule, context)
)
static boolean passCheck(AuthorityRule rule, Context context) {
String requester = context.getOrigin();
// Empty origin or empty limitApp will pass.
if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(rule.getLimitApp())) {
return true;
}
// Do exact match with origin name.
int pos = rule.getLimitApp().indexOf(requester);//包含关系验证
boolean contain = pos > -1;
if (contain) {//equals验证
boolean exactlyMatch = false;
String[] appArray = rule.getLimitApp().split(",");
for (String app : appArray) {
if (requester.equals(app)) {
exactlyMatch = true;
break;
}
}
contain = exactlyMatch;
}
int strategy = rule.getStrategy();
if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {//黑名单验证
return false;
}
if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {//白名单验证
return false;
}
return true;
}
3.这个核心的校验方法内,首先会进行基础判断,Empty origin or empty limitApp will pass.
。然后判断resource
中的rule
,是否已经注册到了rule
中,如果注册了并且能够对应的上,exactlyMatch
参数会被置为true
。
4.拿到了对应结果contain(exactlyMatch)
后,开始进行strategy
的判断。如果是黑名单,则返回contain
;如果是白名单验证则返回!contain
。
作为sentinel
的黑白名单控制,实现的功能也比较简单,只需要我们需要定义控制规则就行了。
多个相同的resource
(name),对应着不同的context
(name),那么我们就可以快速统计出某个资源的总统计信息。对应着多个(归属于同一个resource的)DefaultNode
对应同一个ClusterNode
。
三、SystemSlot分析
1.SystemSlot介绍
官方文档是这样描述SystemSlot的:一句话概括就是系统级限流的控制器
2.源码解读
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {
SystemRuleManager.checkSystem(resourceWrapper);//整个系统限流的切入点
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
1.在下一个slot的开始前,直接调用了com.alibaba.csp.sentinel.slots.system.SystemRuleManager的checkSystem方法。
public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
if (resourceWrapper == null) {
return;
}
// 是否打开校验开关
if (!checkSystemStatus.get()) {
return;
}
// 是否为入口流量
if (resourceWrapper.getEntryType() != EntryType.IN) {
return;
}
//qps 的对比
double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps();
if (currentQps > qps) {
throw new SystemBlockException(resourceWrapper.getName(), "qps");
}
//线程数的对比
int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
if (currentThread > maxThread) {
throw new SystemBlockException(resourceWrapper.getName(), "thread");
}
//获得总成功计数的对比
double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt();
if (rt > maxRt) {
throw new SystemBlockException(resourceWrapper.getName(), "rt");
}
// 按照BBR算法
if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
if (!checkBbr(currentThread)) {
throw new SystemBlockException(resourceWrapper.getName(), "load");
}
}
// cpu状态
if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {
throw new SystemBlockException(resourceWrapper.getName(), "cpu");
}
}
2.进入到校验方案,惯例 的校验了一次resource的非空。
3.开始校验是否打开系统限流开关,流量类型(全局限流只校验入口流量)。
4.进行qps,线程数的校验,当超过任何系统规则的阈值时throws BlockException。
5.进行成功访问数,RT,CPU状态的校验,当超过任何系统规则的阈值时throws BlockException。
四、小结
本期我们讲述了Slot的子类LogSlot
和StatisticSlot
的基本实现原理。
现在建立我们的知识树
实例化DefaultNode和ClusterNode,创建结构树
创建上下文时,首先会在NodeSelectorSlot
中判断是否有DefaultNode
。
如果没有则新增一个基于resource
的DefaultNode
,然后执行下一个slot
。
下一个slot
是ClusterBuilderSlot
,ClusterBuilderSlot
会判断是否有对应的ClusterNode
,如果没有则新增一个基于resource的ClusterNode
并继续下一个流程(slot
)。
总结来说,这个两个slot
奠定了一个基于resource
进行全局控制的基调。
进行信息收集
LogSlot
在DefaultNode
和ClusterNode
初始化后,作为业务实例模块的分界点,收集全局异常并处理。
StatisticSlot
作为全局统计的实例,依托于ClusterNode
,将全局的RT
, QPS
, thread
count
等等信息存放在clusterNodeMap
里面。
进行权限校验及系统级限流
在树结构和信息收集的slot建立完毕后,开始业务逻辑的实现,首先实现的就是AuthoritySlot的黑白名单能力,依托sentinel的resource的定义,我们很简单就可以拿到关于resource的authorityRules,将对应的rules取出后,以此进行黑、白名单判断,也可以理解为一种权限级别的限流措施。
SystemSlot则是全统计的全局限流,从调用点origins级别的配置中读取了配置好的限流措施,在下一个slot实现前完成了所有的判断,如qps,线程数,成功访问数,RT,CPU状态。如果出现异常,则throws BlockException,交给之前的slot去处理相应逻辑。到这里,一个基础的限流框架已经基本实现。