Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

Common fault-tolerant solutions

In the micro-services distributed architecture, service Fault tolerance is a common problem, and we all know there will be more services in the micro-micro-service architecture, and there will be calls relationship between the vast majority of micro-services, if due to an underlying service unavailable resulting in a chain reaction, leading to a series of top service crashes, failures, a phenomenon called avalanche effect or cascading failures. As shown below:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

So in the micro-services distributed architecture, capable of defending against the avalanche effect of fault-tolerant service program is essential, common fault-tolerant scheme is as follows:

1, timeout:

Set request timeout, so that the thread is determined that the request failed request to release the request thread is waiting for more than a certain period of time after, in some scenarios thread releases fast enough, then it will not continue to create threads and lead to resource depletion caused the service crashes

2, limiting:

For example, the image above A service can only withstand about 1k QPS, then set a maximum threshold number of requests, when the reject request 1k QPS reached after this. I only like to eat a bowl of rice, even if I have to I eat three bowls bowl

3, bulkheads mode:

Bulkhead mode is actually borrowed from the cabin structure and design of real life, not so easy to want a boat sink also need to have a certain degree of "fault-tolerant" capability, and early on the ship due to the lack of design, as long as a place water, then the water will gradually diffuse into the whole cabin, the ship of this structure is almost no "fault-tolerant" capability, so it is easier to sink. So some people think this time will be one of the original cabins separated into a separate cabin, use a steel plate welded between cabin separated by death, which is called the steel bulkheads of. With this design, even when one of the two cabin was flooded, it will not affect the other cabins, the ship is still capable of normal driving.

Learn from this idea at the software level, we can make each service runs in its own separate thread pool thread pool is no interference between the service A thread pool resource depletion will not affect the service B. At this time, the thread pool is like cabin bulkhead as different service resources to isolate such a service hang it will not affect other services running

4, breaker mode:

断路器模式的思想实际上和家里的断路器一样,在软件层面大致就是对某个服务的API进行监控,若在一定时间内调用的失败率或失败次数达到指定的阈值就认为该API是不可用的从而触发“跳闸”,即此时断路器就处于打开状态。过了一段时间后断路器会处于一个半开状态,若在半开状态时尝试调用该API成功后就会关闭断路器,否则依旧认为不可用让断路器继续处于打开状态

断路器三态转换如下图:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

断路器模式原文:CircuitBreaker

而Spring Cloud已经提供了相关的服务容错组件,组件里已经整合了这些常用的方案,不需要我们手动去实现。在此之前Spring Cloud提供的唯一服务容错组件是Hystrix,不过现在多了一个选择,那就是Spring Cloud Alibaba的Sentinel组件。关于Hystrix可以参考如下文章,本文主要介绍Sentinel:


Sentinel简介及整合

Sentinel 是什么,官方描述如下:

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的轻量级流量控制、熔断降级框架,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。

官方GitHub仓库地址如下:

https://github.com/alibaba/Sentinel

现在我们来为项目整合Sentinel,第一步添加如下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- actuator,用于暴露监控端点 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Tips:该项目使用的Spring Cloud版本为Greenwich.SR1,Spring Cloud Alibaba版本为0.9.0.RELEASE

第二步配置actuator:

# 暴露所有端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

完成以上两步后,启动项目,使用浏览器访问http://localhost:8080/actuator/sentinel,返回如下结果代表整合成功:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]


搭建Sentinel Dashboard控制台

在上一小节中,我们已经为项目成功整合了Sentinel,但这也只不过是完成了第一步。因为此时没有一个可视化的界面能让我们看到Sentinel具体的监控信息,所以还需要搭建官方提供的可视化Sentinel控制台,然后在控制台中整合项目的监控信息。

Sentinel控制台的下载地址如下:

https://github.com/alibaba/Sentinel/releases

Sentinel Dashboard有多个release版本,应该选择哪个呢?如果你是用在生产环境则选择与项目中sentinel-core版本对应的即可,如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

若只是学习或测试使用那就可以随便选择了,只要能用就行,所以我这里选择最新版本1.6.3,注意这里选择jar包进行下载:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

下载完成后,存放到一个你觉得ok的目录下,然后打开cmd,通过命令运行该jar包。如下:

E:\Spring Cloud Alibaba\Sentinel>java -jar sentinel-dashboard-1.6.3.jar

启动成功,监听的端口是8080:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

使用浏览器访问http://localhost:8080进入到登录页面,默认的账户密码都是sentinel:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

登录成功,此时控制台上是空白的,因为还没有监控任何的项目:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

所以接着到项目中整合一下Sentinel Dashboard的请求地址,在配置文件中添加如下配置:

spring:
  cloud:
    sentinel:
      transport:
        # 配置sentinel控制台的地址
        dashboard: 127.0.0.1:8080

配置完成启动项目后需要先访问一下该项目的接口,因为Sentinel Dashboard是懒加载的,只有监控的项目被访问后才会收集监控信息。这样才能看到下图的实时监控信息,我这里的服务名是content-center:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]


Sentinel 相关配置项小结

客户端(微服务)连接控制台相关配置项:

spring:
  cloud:
    sentinel:
      transport:
        #指定控制台的地址
        dashboard: localhost:8080
        #指定和控制台通信的IP
        #如不配置,会自动选择一个IP注册
        client-ip:  127.0.0.1
        #指定和控制台通信的端口,默认值8719
        #如不设置,会自动从8719开始扫描,依次+1,直到找到未被占用的端口
        port: 8719
        #心跳发送周期,默认值null
        #但在S impleHttpHeartbeatSender会用默认值10秒
        heartbeat- interval-ms :  10000

控制台相关配置项:

配置项 默认值 最小值 描述
sentinel.dashboard.app.hideAppNoMachineMillis 0 60000 是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭
sentinel.dashboard.removeAppNoMachineMillis 0 120000 是否自动删除无健康节点的应用,距离最近一次其下节点的心跳时间毫秒数,默认关闭
sentinel.dashboard.unhealthyMachineMillis 60000 30000 主机失联判定,不可关闭
sentinel.dashboard.autoRemoveMachineMillis 0 300000 距离最近心跳时间超过指定时间是否自动删除失联节点,默认关闭
server.port 8080 - 指定端口
csp.sentinel.dashboard.server localhost:8080 - 指定地址
project.name - - 指定程序的名称
sentinel.dashboard.auth.username [1.6版本支持] sentinel - Sentinel Dashboard登录账号
sentinel.dashboard.auth.password [1.6版本支持] sentinel - Sentinel Dashboard登录密码
server.servlet.session.timeout [1.6版本支持] 30分钟 - 登录Session过期时间。配置为7200表示7200秒;配置为60m表示60分钟

控制台配置项需在启动命令中指定,例如指定账户密码,如下:

java -jar -Dsentinel.dashboard.auth.username=admin -Dsentinel.dashboard.auth.password=123456 sentinel-dashboard-1.6.3.jar

流控规则

我们可以在Sentinel控制台中给某个接口添加流控规则,点击簇点链路,可以看到该服务曾经被访问过的路径:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

然后点击接口右边的流控按钮就可以添加流控规则:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

添加成功:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

此时访问该服务的接口,QPS超过设定的阈值1,就会返回如下信息:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]


关于流控规则中的流控模式:

  • 直接:当前资源的QPS达到设定的阈值,就触发限流
  • 关联:当关联的资源的QPS达到设定的阈值,就触发限流。例如,/shares/1关联了/query,那么/query达到阈值,就会对/shares/1限流
  • 链路:只记录指定链路上的流量,这种模式是针对接口级别的来源进行限流

链路模式稍微有些抽象,这里举个简单的例子说明一下。下图中有两个调用链路,图中的/test-b/test-a实际就是两个接口,它们都调用了同一个common资源,所以/test-b/test-a就称为common的入口资源:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

此时我为common添加一个限流规则如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

可以看到流控模式选择链路后,需要填写一个入口资源,我这里填的是/test-a,那么这意味着什么呢?意味着当/test-a的QPS达到该规则的阈值后,就会对/test-a限流,同时/test-b不会受到任何影响。说明这种流控模式可以针对接口级别的来源进行限流,而“针对来源”则是对微服务级别的来源进行限流。

关于流控规则中的监控效果:

  • 快速失败:直接失败,抛出异常,不做任何额外的处理,是最简单的效果
    • 相关源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
  • Warm Up(预热):会根据codeFactor(默认3)的值,从阈值除以codeFactor,经过预热时长,才到达设置的QPS阈值。适用于将突然增大的流量转换为缓步增长的场景
    • 相关的官方文档:限流 - 冷启动
    • 相关源码:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
  • 排队等待:匀速排队,让请求以均匀的速度通过,若请求等待时间超过设置的超时时间则抛弃该请求,阈值类型必须设置成QPS,否则无效。适用于突发流量的场景
    • 相关的官方文档:限流 - 匀速器
    • 相关源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

关于流控的官方文档:


降级规则

服务降级实际就是断路器模式的应用,相对于流控规则,降级规则要简单一些。降级规则可以在“簇点链路”或“降级规则”中添加:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

例如,这里给/shares/1添加降级规则,降级策略先以RT为例:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

该降级规则的含义如下图:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

此时访问/shares/1接口,秒级平均响应时间超出阈值1,并且在时间窗口内通过的请求大于等于5,就会返回如下信息:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

关于RT这种降级策略需要注意的点:

  • RT默认最大为4900ms,所以即便设置的值大于4900ms也依旧会按照4900ms计算
    • 可以通过参数修改:-Dcsp.sentinel.statistic.max.rt=xxx

若将降级策略改为异常比例,则含义如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

若将降级策略改为异常数,则含义如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

关于异常数这种降级策略需要注意的点:

  • 若将时间窗口的值设置小于60秒则可能会出问题,因为异常数的统计是分钟级别的,时间窗口小于60秒就有可能不断进入降级状态

降级规则的相关源码:

  • com.alibaba.csp.sentinel.slots.block.degradeDegradeRule#passCheck(对降级的判断都在这个方法里完成)

在文章的开头我们介绍过断路器有三个状态,所以这里需要提及一下的是目前Sentinel的降级断路器是不支持半开状态的,只有打开和关闭两个状态,据官方人员描述说是会预计在未来添加半开的支持。

关于降级的官方文档:


热点规则

热点规则全称是热点参数限流规则,从名称可以得知,需要有参数的接口才能够使用热点规则。例如,有一个接口的代码如下:

@GetMapping("/test-hot")
@SentinelResource("hot")  // 该注解用于声明是Sentinel需要监控的资源
public String testHot(@RequestParam(required = false) String a,
                      @RequestParam(required = false) String b) {
    return a + " " + b;
}

在控制台中为hot添加热点规则,如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

  • Tips:参数索引从0开始,对应到代码中的话,则参数a的索引为0,参数b的索引为1,所以该规则是作用于参数a

添加完该规则后,此时访问这个接口,两个参数都传值,当QPS达到阈值时,就会抛出如下异常信息:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

如果不传参数a,仅传参数b的话,则不会受到该规则的限流,如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

说明该规则表达的含义是:在时间窗口内,一旦该规则指定的索引参数QPS达到了阈值,则会触发限流

除此之外,还有高级选项,在这里可以添加参数例外项,如下示例:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

添加完成后,此时将参数a的值设置为5,然后频繁发送请求,会发现即便QPS超过1也不会触发限流:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

这是因为参数a的值设置为5时,限流阈值是1000,设置为其他值时,限流阈值才是1。这就是所谓的参数例外项了,即参数的为某个特定的值时,只受参数例外项里的限流阈值影响。

热点规则适用的场景:

  • 适用于存在热点参数并希望提升API可用性的场景,即某个特定请求参数QPS偏高于其他请求参数时,仅对该参数的请求限流,使用其他请求参数则可以正常响应,这样可以提高一定的可用性

使用热点规则需要注意的点:

  • 参数必须是基本类型或者String类型,否则将不会生效

热点规则相关源码:

  • com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker#passCheck(对热点参数规则的判断逻辑都在这个方法里)

系统规则

系统规则全称为系统保护规则,从名称可以得知该规则是用于保护系统、防止系统负载过高而崩溃的,所以触发系统规则后会对整个系统限流。添加系统规则如下图所示:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

设置系统规则比较简单,选择一个合适的阈值类型并填写阈值即可:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

关于阈值类型:

  • LOAD(负载):当系统load1(1分钟的load)超过阈值,且并发线程数超过系统容量时触发,建议设置为CPU核心数 2.5(注意:仅对 Linux/Unit-like 机器生效)。例如CPU核心数为4,`4 2.5 = 10`
    • 系统容量 = maxQPS * minRT;(由Sentinel计算 )
      • maxQPS:秒级统计出来的最大QPS
      • minRT:秒级统计出来的最小响应时间
    • 相关源码:com.alibaba.csp.sentinel.slots.system.SystemRuleManager#checkBbr
  • RT:所有入口流量的平均RT达到阈值时触发
  • 线程数:所有入口流量的并发线程数达到阈值时触发
  • 入口QPS:所有入口流量的QPS达到阈值时触发
  • CPU使用率:系统CPU使用率达到阈值时触发

系统规则的判断逻辑所在的源码如下:

  • com.alibaba.csp.sentinel.slots.system.SystemRuleManager#checkSystem

授权规则

授权规则用于限制某个资源仅允许哪个服务访问,所以通常用于对服务消费者的访问权进行控制。我们可以在簇点链路中为某个接口添加授权规则,这里以/shares/1接口为例,如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

新增授权规则:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

  • 该授权规则的含义为:仅允许test服务访问/shares/1接口,如果授权类型设置为黑名单则表示/shares/1接口不允许test服务访问。即白名单是授权某个服务访问,黑名单则是限制某个服务访问,从而实现访问控制的效果。

代码配置规则

上面几个关于规则的小节中已经介绍了如何在Sentinel控制台中配置各种规则,除此之外,Sentinel还支持在代码中配置这些规则,所以本小节将简单介绍一下如何在代码中进行配置。

代码如下(Tips:代码基于sentinel-core 1.5.2版本):

package com.zj.node.contentcenter.controller.content;

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 添加Sentinel规则
 *
 * @author 01
 * @date 2019-07-31
 **/
@Slf4j
@RestController
public class SentinelRuleController {

    /**
     * 测试添加流控规则
     */
    @PostMapping("/test-add-flow-rule")
    public String testAddFlowRile(String resourceName) {
        log.info("add flow rule. resourceName is {}", resourceName);
        addFlowQpsRule(resourceName);

        return "add flow rule success!";
    }

    /**
     * 添加流控规则
     *
     * @param resourceName 资源名称
     */
    private void addFlowQpsRule(String resourceName) {
        // 规则列表
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule(resourceName);
        // 针对来源
        rule.setLimitApp("default");
        // 设置阈值类型为QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 单机阈值
        rule.setCount(20);
        // 将规则添加到规则列表
        rules.add(rule);
        // 加载规则列表
        FlowRuleManager.loadRules(rules);
    }

    /**
     * 添加降级规则
     *
     * @param resourceName 资源名称
     */
    private void addDegradeRule(String resourceName) {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule(resourceName);
        // 设置降级策略为 RT
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        // set threshold RT, 10 ms(设置RT时间阈值)
        rule.setCount(10);
        // 时间窗口
        rule.setTimeWindow(10);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }

    /**
     * 添加热点规则
     *
     * @param resourceName 资源名称
     */
    private void addHotRule(String resourceName) {
        ParamFlowRule rule = new ParamFlowRule(resourceName);
        // 参数索引
        rule.setParamIdx(0);
        // 单机阈值
        rule.setCount(5);

        // 添加参数例外项
        ParamFlowItem item = new ParamFlowItem();
        // 参数类型
        item.setClassType(int.class.getName());
        // 参数值
        item.setObject("5");
        // 限流阈值
        item.setCount(10);
        rule.setParamFlowItemList(Collections.singletonList(item));

        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }

    /**
     * 添加系统规则
     */
    private void addSystemRule() {
        List<SystemRule> rules = new ArrayList<>();
        SystemRule rule = new SystemRule();
        // 设置系统最高负载阈值
        rule.setHighestSystemLoad(10);
        rules.add(rule);
        SystemRuleManager.loadRules(rules);
    }

    /**
     * 添加授权规则
     *
     * @param resourceName 资源名称
     * @param limitApp     流控应用(指调用方,多个调用方名称使用英文逗号分隔)
     */
    private void addAuthorityRule(String resourceName, String limitApp) {
        AuthorityRule rule = new AuthorityRule();
        // 资源名称
        rule.setResource(resourceName);
        // 流控应用
        rule.setLimitApp(limitApp);
        // 设置授权类型为白名单
        rule.setStrategy(RuleConstant.AUTHORITY_WHITE);

        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
    }
}

我们来测试添加流控规则,使用postman访问测试接口,如下:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

添加成功后,到Sentinel控制台中,查看是否存在该规则:
Spring Cloud Alibaba's service fault-tolerant components - Sentinel [Basics]

从上图中可以看到该流控规则已经成功添加到Sentinel中了,证明测试成功。至于其他的规则也可以使用类似的方式添加,并且也都给出了代码,这里就不一一去演示了。


Sentinel规则参数总结

下面总结一下Alibaba Sentinel各种规则的参数,并且提供了官方文档的链接,若未来本文不再适用,可以自行点击链接前往官方文档查看

1、流控规则:

Field 说明 默认值
resource 资源名,资源名是限流规则的作用对象
count 限流阈值
grade 限流阈值类型,QPS 或线程数模式 QPS 模式
limitApp 流控针对的调用来源 default,代表不区分调用来源
strategy 判断的根据是资源自身,还是根据其它关联资源 (refResource),还是根据链路入口 根据资源本身
controlBehavior 流控效果(直接拒绝 / 排队等待 / 慢启动模式) 直接拒绝

官方文档:


2、降级规则:

Field 说明 默认值
resource 资源名,即限流规则的作用对象
count 阈值
grade 降级模式,根据 RT 降级还是根据异常比例或异常数降级 RT
timeWindow 降级的时间,单位为 s

官方文档:


3、热点规则:

Field 说明 默认值
resource 资源名,即热点规则的作用对象
count 限流阈值,必填
grade 限流模式 QPS 模式
durationInSec 统计窗口时间长度(单位为秒),1.6.0 版本开始支持 1s
controlBehavior 流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持 快速失败
maxQueueingTimeMs Long waiting maximum queuing (to take effect only in the uniform line mode), available since version 1.6.0 0ms
paramIdx Index hot parameters required, the corresponding parameter index position SphU.entry (xxx, args) no
paramFlowItemList Exceptions parameter, the parameter value may be set for the individual specified limit threshold, unlimited front count threshold. It supports only basic types, and strings no
clusterMode Whether it is a cluster parameters of flow control rules false
clusterConfig Flow control cluster configuration no

The official document:


4, system rules:

Field Explanation Defaults
highestSystemLoad The largest load1, the reference value 1 (not effective)
avgRt The average response time for all inlet flows 1 (not effective)
maxThread The maximum number of concurrent incoming traffic 1 (not effective)
qps All entrances resources QPS 1 (not effective)

The official document:


5, authorization rules:

Field Explanation Defaults
resource Role of the object resource name, that authorization rules no
Limitapp Application of flow control (of the caller, i.e. the service consumer), corresponding to the blacklist / whitelist, different Origin (comma ,) separated, asappA,appB no
strategy Restricted mode, AUTHORITY_WHITEwhitelist mode, AUTHORITY_BLACKblacklist mode, whitelist mode by default AUTHORITY_WHITE

The official document:

Guess you like

Origin blog.51cto.com/zero01/2425570