如何使用

Eric Zhao edited this page Jan 30, 2019 · 56 revisions

目录

简介

Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果。

这篇文章主要介绍 Sentinel 核心库的使用。如果希望有一个最快最直接的了解,可以参考 新手指南 来获取一个最直观的感受。

我们说的资源,可以是任何东西,服务,服务里的方法,甚至是一段代码。使用 Sentinel 来进行资源保护,主要分为几个步骤:

  1. 定义资源
  2. 定义规则
  3. 检验规则是否生效

先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。

对于主流的框架,我们提供适配,只需要按照适配中的说明配置,Sentinel 就会默认定义提供的服务,方法等为资源。

定义资源

方式一:主流框架的默认适配

为了减少开发的复杂程度,我们对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC 等都做了适配。您只需要引入对应的依赖,它们的方法,服务,都会进行默认的埋点。可以参见: 主流框架的适配

方式二:抛出异常的方式定义资源

用这种方式,当资源发生了限流之后会抛出 BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。示例代码如下:

Entry entry = null;
// 务必保证finally会被执行
try {
  // 资源名可使用任意有业务语义的字符串
  entry = SphU.entry("自定义资源名");
  // 被保护的业务逻辑
  // do something...
} catch (BlockException e1) {
  // 资源访问阻止,被限流或被降级
  // 进行相应的处理操作
} finally {
  if (entry != null) {
    entry.exit();
  }
}

注意: SphU.entry(xxx) 需要与 entry.exit() 方法成对出现,匹配调用,否则会导致调用链记录异常,抛出 ErrorEntryFreeException 异常。

方式三:返回布尔值方式定义资源

用这种方式,当资源发生了限流之后会返回 false,这个时候可以根据返回值,进行限流之后的逻辑处理。示例代码如下:

  // 资源名可使用任意有业务语义的字符串
  if (SphO.entry("自定义资源名")) {
    // 务必保证finally会被执行
    try {
      /**
      * 被保护的业务逻辑
      */
    } finally {
      SphO.exit();
    }
  } else {
    // 资源访问阻止,被限流或被降级
    // 进行相应的处理操作
  }

方式四:注解方式定义资源

Sentinel 支持通过 @SentinelResource 注解定义资源并配置 blockHandlerfallback 函数来进行限流之后的处理。示例:

// 原本的业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
    throw new RuntimeException("getUserById command failed");
}

// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {
    return new User("admin");
}

注意 blockHandler 函数会在原方法被限流/降级/系统保护的时候调用,而 fallback 函数仅会在原方法被降级时作为 fallback 方法,其余时候不会被调用。

更多指引可以参见 Sentinel 注解支持文档

方式五:异步调用支持

Sentinel 从 0.2.0 版本开始支持异步调用链路的统计。在异步调用中,需要通过 SphU.asyncEntry(xxx) 方法定义资源,并通常需要在异步的回调函数中调用 exit 方法。以下是一个简单的示例:

try {
    AsyncEntry entry = SphU.asyncEntry(resourceName);

    // 异步调用.
    doAsync(userId, result -> {
        try {
            // 在此处处理异步调用的结果.
        } finally {
            // 在回调结束后 exit.
            entry.exit();
        }
    });
} catch (BlockException ex) {
    // Request blocked.
    // Handle the exception (e.g. retry or fallback).
}

SphU.asyncEntry(xxx) 不会影响当前(调用线程)的 Context,因此以下两个 entry 在调用链上是平级关系(处于同一层),而不是嵌套关系:

// 调用链类似于:
// -parent
// ---asyncResource
// ---syncResource
asyncEntry = SphU.asyncEntry(asyncResource);
entry = SphU.entry(normalResource);

若在异步回调中需要嵌套其它的资源调用(无论是 entry 还是 asyncEntry),只需要借助 Sentinel 提供的上下文切换功能,在对应的地方通过 ContextUtil.runOnContext(context, f) 进行 Context 变换,将对应资源调用处的 Context 切换为生成的异步 Context,即可维持正确的调用链路关系。示例如下:

public void handleResult(String result) {
    Entry entry = null;
    try {
        entry = SphU.entry("handleResultForAsync");
        // Handle your result here.
    } catch (BlockException ex) {
        // Blocked for the result handler.
    } finally {
        if (entry != null) {
            entry.exit();
        }
    }
}

public void someAsync() {
    try {
        AsyncEntry entry = SphU.asyncEntry(resourceName);

        // Asynchronous invocation.
        doAsync(userId, result -> {
            // 在异步回调中进行上下文变换,通过 AsyncEntry 的 getAsyncContext 方法获取异步 Context
            ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
                try {
                    // 此处嵌套正常的资源调用.
                    handleResult(result);
                } finally {
                    entry.exit();
                }
            });
        });
    } catch (BlockException ex) {
        // Request blocked.
        // Handle the exception (e.g. retry or fallback).
    }
}

此时的调用链就类似于:

-parent
---asyncInvocation
-----handleResultForAsync

更详细的示例可以参考 Demo 中的 AsyncEntryDemo,里面包含了普通资源与异步资源之间的各种嵌套示例。

规则的种类

Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。

Sentinel 支持以下几种规则:流量控制规则熔断降级规则系统保护规则授权规则热点参数规则

流量控制规则 (FlowRule)

流量规则的定义

重要属性:

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

同一个资源可以同时有多个限流规则。

通过代码定义流量控制规则

理解上面规则的定义之后,我们可以通过调用 FlowRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则,比如:

private static void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource(KEY);
        // set limit qps to 20
        rule1.setCount(20);
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule1.setLimitApp("default");
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
    }

更多详细内容可以参考 流量控制

熔断降级规则 (DegradeRule)

熔断降级规则包含下面几个重要的属性:

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

同一个资源可以同时有多个降级规则。

理解上面规则的定义之后,我们可以通过调用 DegradeRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则。

private static void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    rule.setResource(KEY);
    // set threshold RT, 10 ms
    rule.setCount(10);
    rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
    rule.setTimeWindow(10);
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

更多详情可以参考 熔断降级

系统保护规则 (SystemRule)

规则包含下面几个重要的属性:

Field 说明 默认值
highestSystemLoad 最大的 load1,参考值 -1 (不生效)
avgRt 所有入口流量的平均响应时间 -1 (不生效)
maxThread 入口流量的最大并发数 -1 (不生效)
qps 所有入口资源的 QPS -1 (不生效)

理解上面规则的定义之后,我们可以通过调用 SystemRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则。

private void initSystemRule() {
    List<SystemRule> rules = new ArrayList<>();
    SystemRule rule = new SystemRule();
    rule.setHighestSystemLoad(10);
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}

更多详情可以参考 系统自适应保护

授权规则 (AuthorityRule)

很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的黑白名单控制的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

授权规则,即黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:

  • resource:资源名,即限流规则的作用对象
  • limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB
  • strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式

更多详情可以参考 黑白名单控制

热点规则 (ParamFlowRule)

详情可以参考 热点参数限流

查询更改规则

运行下面命令,则会返回现有生效的规则:

curl http://localhost:8719/getRules?type=<XXXX>

其中,type=flow 以 JSON 格式返回现有的限流规则;degrade 则返回现有生效的降级规则列表;system 则返回系统保护规则。

同时也可以通过下面命令来修改已有规则:

curl http://localhost:8719/setRules?type=<XXXX>&data=<DATA>

其中,type 可以输入 flowdegrade 等方式来制定更改的规则种类,data 则是对应的 JSON 格式的规则。

定制自己的持久化规则

上面的规则配置,都是存在内存中的。即如果应用重启,这个规则就会失效。因此我们提供了开放的接口,您可以通过实现 DataSource 接口的方式,来自定义规则的存储数据源。通常我们的建议有:

  • 整合动态配置系统,如 ZooKeeper、Nacos 等,动态地实时刷新配置规则
  • 结合 RDBMS、NoSQL、VCS 等来实现该规则
  • 配合 Sentinel Dashboard 使用

更多详情请参考 动态规则配置

规则生效的效果

判断限流降级异常

通过以下方法判断:

BlockException.isBlockException(Throwable t);

除了在业务代码逻辑上看到规则生效,我们也可以通过下面简单的方法,来校验规则生效的效果:

  • 暴露的 HTTP 接口:通过运行下面命令 curl http://localhost:8719/cnode?id=<资源名称>,观察返回的数据。如果规则生效,在返回的数据栏中的 block 以及 block(m) 中会有显示
  • 日志:Sentinel 提供秒级的资源运行日志以及限流日志,详情可以参考: 日志

其它 API

业务异常统计 Tracer

业务异常记录类 Tracer 用于记录业务异常。相关方法:

  • public static void trace(Throwable e):记录业务异常(非 BlockException 异常)
  • public static void trace(Throwable e, int count):记录业务异常,异常数目为传入的 count

如果用户通过 SphUSphO 手动定义资源,则 Sentinel 不能感知上层业务的异常,需要手动调用 Tracer.trace(ex) 来记录业务异常,否则对应的异常不会统计到 Sentinel 异常计数中。

从 1.3.1 版本开始,注解方式定义资源支持自动统计业务异常,无需手动调用 Tracer.trace(ex) 来记录业务异常。Sentinel 1.3.1 以前的版本需要手动记录。

上下文工具类 ContextUtil

相关静态方法:

标识进入调用链入口(上下文)

以下静态方法用于标识调用链路入口,用于区分不同的调用链路:

  • public static Context enter(String contextName)
  • public static Context enter(String contextName, String origin)

其中 contextName 代表调用链路入口名称(上下文名称),origin 代表调用来源名称。默认调用来源为空。返回值类型为 Context,即生成的调用链路上下文对象。

注意ContextUtil.enter(xxx) 方法仅在调用链路入口处生效,即仅在当前线程的初次调用生效,后面再调用不会覆盖当前线程的调用链路,直到 exit。Context 存于 ThreadLocal 中,因此切换线程时可能会丢掉,如果需要跨线程使用可以结合 runOnContext 方法使用。

流控规则中若选择“流控方式”为“链路”方式,则入口资源名即为上面的 contextName

退出调用链(清空上下文)

  • public static void exit():该方法用于退出调用链,清理当前线程的上下文。

获取当前线程的调用链上下文

  • public static Context getContext():获取当前线程的调用链路上下文对象。

在某个调用链上下文中执行代码

  • public static void runOnContext(Context context, Runnable f):常用于异步调用链路中 context 的变换。

指标统计配置

Sentinel 底层采用高性能的滑动窗口数据结构来统计实时的秒级指标数据,并支持对滑动窗口进行配置。主要有以下两个配置:

  • windowIntervalMs:滑动窗口的总的时间长度,默认为 1000 ms
  • sampleCount:滑动窗口划分的格子数目,默认为 2;格子越多则精度越高,但是内存占用也会越多

sliding-window-leap-array

我们可以通过 SampleCountProperty 来动态地变更滑动窗口的格子数目,通过 IntervalProperty 来动态地变更滑动窗口的总时间长度。注意这两个配置都是全局生效的,会影响所有资源的所有指标统计。

Dashboard

详情请参考:Sentinel Dashboard 文档

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.