前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《一起学sentinel》五、Slot的子类及实现之AuthoritySlot和SystemSlot

《一起学sentinel》五、Slot的子类及实现之AuthoritySlot和SystemSlot

原创
作者头像
M4Y
修改2020-09-22 17:56:44
5870
修改2020-09-22 17:56:44
举报
文章被收录于专栏:一起学sentinel一起学sentinel

一、概述

在 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 的访问设置白名单,只有来源为 appAappB 的请求才可通过,则可以配置如下白名单规则: AuthorityRule rule = new AuthorityRule(); rule.setResource("test"); rule.setStrategy(RuleConstant.AUTHORITY_WHITE); rule.setLimitApp("appA,appB"); AuthorityRuleManager.loadRules(Collections.singletonList(rule));

2.源码解读
代码语言:javascript
复制
@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阶段,执行了一个校验方法.

代码语言:javascript
复制
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<AuthorityRule>),循环这个rules,在循环里面调用校验方法(AuthorityRuleChecker.passCheck(rule, context)

代码语言:javascript
复制
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 的黑白名单控制,实现的功能也比较简单,只需要我们需要定义控制规则就行了。

三、SystemSlot分析

1.SystemSlot介绍

官方文档是这样描述SystemSlot的:一句话概括就是系统级限流的控制器

2.源码解读
代码语言:javascript
复制
@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方法。

代码语言:javascript
复制
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的子类LogSlotStatisticSlot的基本实现原理。

现在建立我们的知识树

实例化DefaultNode和ClusterNode,创建结构树

创建上下文时,首先会在NodeSelectorSlot中判断是否有DefaultNode

如果没有则新增一个基于resourceDefaultNode,然后执行下一个slot

下一个slotClusterBuilderSlotClusterBuilderSlot会判断是否有对应的ClusterNode,如果没有则新增一个基于resource的ClusterNode并继续下一个流程(slot)。

总结来说,这个两个slot奠定了一个基于resource进行全局控制的基调。

进行信息收集

LogSlotDefaultNodeClusterNode初始化后,作为业务实例模块的分界点,收集全局异常并处理。

StatisticSlot作为全局统计的实例,依托于ClusterNode,将全局的RT, QPS, thread count 等等信息存放在clusterNodeMap里面。

进行权限校验及系统级限流

在树结构和信息收集的slot建立完毕后,开始业务逻辑的实现,首先实现的就是AuthoritySlot的黑白名单能力,依托sentinel的resource的定义,我们很简单就可以拿到关于resource的authorityRules,将对应的rules取出后,以此进行黑、白名单判断,也可以理解为一种权限级别的限流措施。

SystemSlot则是全统计的全局限流,从调用点origins级别的配置中读取了配置好的限流措施,在下一个slot实现前完成了所有的判断,如qps,线程数,成功访问数,RT,CPU状态。如果出现异常,则throws BlockException,交给之前的slot去处理相应逻辑。到这里,一个基础的限流框架已经基本实现。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、概述
    • 二、AuthoritySlot分析
      • 三、SystemSlot分析
        • 四、小结
        相关产品与服务
        iOA 应用安全访问服务
        iOA 应用安全访问服务(Application Secure Access Service)是一款基于零信任架构的应用安全访问云平台,为企业提供安全接入数据中心(本地、单云、混合云)的解决方案。iOA 应用安全访问服务依托腾讯云全球加速节点,为企业员工提供快速、稳定的访问体验,适用于远程办公、数据中心接入、权限控制、终端管控等多种业务场景。同时,iOA 应用安全访问服务支持对接企业微信,通过企业微信安全访问内网应用,接入简单、安全可靠、管控全面可视化。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档