Sentinel 流量控制 熔断降级 初探

Stella981
• 阅读 652

    还记得之前写过一篇防雪崩利器:熔断器 Hystrix 的原理与使用https://my.oschina.net/u/3266761/blog/2654470,讲述了服务降级和熔断的控制,今天带来另一个流量控制与服务降级阿里开源框架sentinel。

    首先是两者的对比:

    Hystrix 的关注点在于以隔离和熔断为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制。

    而 Sentinel 的侧重点在于:

  • 多样化的流量控制

  • 熔断降级

  • 系统负载保护

  • 实时监控和控制台

可以看到两者解决的问题还是有比较大的不同的,下面我们来具体对比一下。

一、共同特性

1、资源模型和执行模型上的对比

Hystrix 的资源模型设计上采用了命令模式,将对外部资源的调用和 fallback 逻辑封装成一个命令对象(HystrixCommand/ HystrixObservableCommand),其底层的执行是基于 RxJava 实现的。每个 Command 创建时都要指定 commandKey 和 groupKey(用于区分资源)以及对应的隔离策略(线程池隔离 or 信号量隔离)。线程池隔离模式下需要配置线程池对应的参数(线程池名称、容量、排队超时等),然后 Command 就会在指定的线程池按照指定的容错策略执行;信号量隔离模式下需要配置最大并发数,执行 Command 时 Hystrix 就会限制其并发调用。

Sentinel 的设计则更为简单。相比 Hystrix Command 强依赖隔离规则,Sentinel 的资源定义与规则配置的耦合度更低。Hystrix 的 Command 强依赖于隔离规则配置的原因是隔离规则会直接影响 Command 的执行。在执行的时候 Hystrix 会解析 Command 的隔离规则来创建 RxJava Scheduler 并在其上调度执行,若是线程池模式则 Scheduler 底层的线程池为配置的线程池,若是信号量模式则简单包装成当前线程执行的 Scheduler。

而Sentinel则不一样,开发的时候只需要考虑这个方法/代码是否需要保护,置于用什么来保护,可以任何时候动态实时的区修改。

从 0.1.1 版本开始,Sentinel 还支持基于注解的资源定义方式,可以通过注解参数指定异常处理函数和 fallback 函数。Sentinel 提供多样化的规则配置方式。除了直接通过 loadRules API 将规则注册到内存态之外,用户还可以注册各种外部数据源来提供动态的规则。用户可以根据系统当前的实时情况去动态地变更规则配置,数据源会将变更推送至 Sentinel 并即时生效。

2、隔离设计上的对比

隔离是 Hystrix 的核心功能之一。Hystrix 提供两种隔离策略:线程池隔离(Bulkhead Pattern)和信号量隔离,其中最推荐也是最常用的是线程池隔离。Hystrix 的线程池隔离针对不同的资源分别创建不同的线程池,不同服务调用都发生在不同的线程池中,在线程池排队、超时等阻塞情况时可以快速失败,并可以提供 fallback 机制。线程池隔离的好处是隔离度比较高,可以针对某个资源的线程池去进行处理而不影响其它资源,但是代价就是线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。

但是,实际情况下,线程池隔离并没有带来非常多的好处。最直接的影响,就是会让机器资源碎片化。考虑这样一个常见的场景,在 Tomcat 之类的 Servlet 容器使用 Hystrix,本身 Tomcat 自身的线程数目就非常多了(可能到几十或一百多),如果加上 Hystrix 为各个资源创建的线程池,总共线程数目会非常多(几百个线程),这样上下文切换会有非常大的损耗。另外,线程池模式比较彻底的隔离性使得 Hystrix 可以针对不同资源线程池的排队、超时情况分别进行处理,但这其实是超时熔断和流量控制要解决的问题,如果组件具备了超时熔断和流量控制的能力,线程池隔离就显得没有那么必要了。

Hystrix 的信号量隔离限制对某个资源调用的并发数。这样的隔离非常轻量级,仅限制对某个资源调用的并发数,而不是显式地去创建线程池,所以 overhead 比较小,但是效果不错。但缺点是无法对慢调用自动进行降级,只能等待客户端自己超时,因此仍然可能会出现级联阻塞的情况

Sentinel 可以通过并发线程数模式的流量控制来提供信号量隔离的功能。并且结合基于响应时间的熔断降级模式,可以在不稳定资源的平均响应时间比较高的时候自动降级,防止过多的慢调用占满并发数,影响整个系统。

3、熔断降级的对比

Sentinel 和 Hystrix 的熔断降级功能本质上都是基于熔断器模式(Circuit Breaker Pattern)。Sentinel 与 Hystrix 都支持基于失败比率(异常比率)的熔断降级,在调用达到一定量级并且失败比率达到设定的阈值时自动进行熔断,此时所有对该资源的调用都会被 block,直到过了指定的时间窗口后才启发性地恢复。上面提到过,Sentinel 还支持基于平均响应时间的熔断降级,可以在服务响应时间持续飙高的时候自动熔断,拒绝掉更多的请求,直到一段时间后才恢复。这样可以防止调用非常慢造成级联阻塞的情况。

4、实时指标统计实现的对比

    Hystrix 和 Sentinel 的实时指标数据统计实现都是基于滑动窗口的。Hystrix 1.5 之前的版本是通过环形数组实现的滑动窗口,通过锁配合 CAS 的操作对每个桶的统计信息进行更新。Hystrix 1.5 开始对实时指标统计的实现进行了重构,将指标统计数据结构抽象成了响应式流(reactive stream)的形式,方便消费者去利用指标信息。同时底层改造成了基于 RxJava 的事件驱动模式,在服务调用成功/失败/超时的时候发布相应的事件,通过一系列的变换和聚合最终得到实时的指标统计数据流,可以被熔断器或 Dashboard 消费。

Sentinel 目前抽象出了 Metric 指标统计接口,底层可以有不同的实现,目前默认的实现是基于 LeapArray 的滑动窗口,后续根据需要可能会引入 reactive stream 等实现。

二、Sentinel 特性

除了之前提到的两者的共同特性之外,Sentinel 还提供以下的特色功能:

1、轻量级、高性能

Sentinel 作为一个功能完备的高可用流量管控组件,其核心 sentinel-core 没有任何多余依赖,打包后只有不到 200 KB,非常轻量级。开发者可以放心地引入 sentinel-core 而不需担心依赖问题。同时,Sentinel 提供了多种扩展点,用户可以很方便地根据需求去进行扩展,并且无缝地切合到 Sentinel 中。

引入 Sentinel 带来的性能损耗非常小。只有在业务单机量级超过 25W QPS 的时候才会有一些显著的影响(5% - 10% 左右),单机 QPS 不太大的时候损耗几乎可以忽略不计。

2、流量控制

Sentinel 可以针对不同的调用关系,以不同的运行指标(如 QPS、并发调用数、系统负载等)为基准,对资源调用进行流量控制,将随机的请求调整成合适的形状。

Sentinel 支持多样化的流量整形策略,在 QPS 过高的时候可以自动将流量调整成合适的形状。常用的有:

  • 直接拒绝模式:即超出的请求直接拒绝。

  • 慢启动预热模式:当流量激增的时候,控制流量通过的速率,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

  • 匀速器模式:利用 Leaky Bucket 算法实现的匀速模式,严格控制了请求通过的时间间隔,同时堆积的请求将会排队,超过超时时长的请求直接被拒绝。Sentinel 还支持基于调用关系的限流,包括基于调用方限流、基于调用链入口限流、关联流量限流等,依托于 Sentinel 强大的调用链路统计信息,可以提供精准的不同维度的限流。

3、系统负载保护

    Sentinel 对系统的维度提供保护,负载保护算法借鉴了 TCP BBR 的思想。当系统负载较高的时候,如果仍持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

4、实时监控和控制面板

    Sentinel 提供 HTTP API 用于获取实时的监控信息,如调用链路统计信息、簇点信息、规则信息等。如果用户正在使用 Spring Boot/Spring Cloud 并使用了Sentinel Spring Cloud Starter,还可以方便地通过其暴露的 Actuator Endpoint 来获取运行时的一些信息,如动态规则等。未来 Sentinel 还会支持标准化的指标监控 API,可以方便地整合各种监控系统和可视化系统,如 Prometheus、Grafana 等。

    Sentinel控制台(Dashboard)提供了机器发现、配置规则、查看实时监控、查看调用链路信息等功能,使得用户可以非常方便地去查看监控和进行配置。

5、生态

    Sentinel 目前已经针对 Servlet、Dubbo、Spring Boot/Spring Cloud、gRPC 等进行了适配,用户只需引入相应依赖并进行简单配置即可非常方便地享受 Sentinel 的高可用流量防护能力。未来     Sentinel 还会对更多常用框架进行适配,并且会为 Service Mesh 提供集群流量防护的能力。

三、总结

    Sentinel 流量控制 熔断降级 初探

四、具体实现

    Sentinel 介绍

    随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。

  • 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

  • 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。

如何使用 Sentinel

    如果要在您的项目中引入 Sentinel,使用 group ID 为 org.springframework.cloud 和 artifact ID 为 spring-cloud-starter-alibaba-sentinel 的 starter。    

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
   <version>0.2.1.RELEASE</version>
</dependency>

    下面这个例子就是一个最简单的使用 Sentinel 的例子:

package cn.chinotan.controller;

import cn.chinotan.feign.EchoService;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @program: test
 * @description: 最简单的使用 Sentinel 的例子
 * @author: xingcheng
 * @create: 2019-01-12 18:01
 **/
@RestController
@RequestMapping("/sentinel")
public class SentinelController {
    @GetMapping(value = "/hello")
    @SentinelResource(value = "hello", fallback = "helloFallback")
    public String hello() {
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Hello Sentinel";
    }

    public String helloFallback() {
        return "helloFallback";
    }
}

@SentinelResource 注解用来标识资源是否被限流、降级。上述例子上该注解的属性 'hello' 表示资源名。

@SentinelResource 还提供了其它额外的属性如 blockHandlerblockHandlerClassfallback用于表示限流或降级的操作

从 0.1.1 版本开始,Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等

@SentinelResource 注解

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

  • value: 资源名称,必需项(不能为空)
  • entryType: 入口类型,可选项(默认为 EntryType.OUT
  • blockHandlerblockHandlerClassblockHandler 对应处理 BlockException 的函数名称,可选项。若未配置,则将 BlockException 直接抛出。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback: fallback 函数名称,可选项,仅针对降级功能生效(DegradeException)。fallback 函数的访问范围需要是 public,参数类型和返回类型都需要与原方法相匹配,并且需要和原方法在同一个类中。业务异常不会进入 fallback 逻辑。

若 blockHandler 和 fallback 都进行了配置,则遇到降级的时候首先选择 fallback 函数进行处理。

注意 blockHandler 是处理被 block 的情况(所有类型的 BlockException),而 fallback 仅处理被降级的情况(DegradeException)。其它异常会原样抛出,Sentinel 不会进行处理。

示例:

public class TestService {

    // 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.
    @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
    public void test() {
        System.out.println("Test");
    }

    // 原函数
    @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
    public String hello(long s) {
        return String.format("Hello at %d", s);
    }
    
    // Fallback 函数,函数签名与原函数一致.
    public String helloFallback(long s) {
        return String.format("Halooooo %d", s);
    }

    // Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
    public String exceptionHandler(long s, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return "Oops, error occurred at " + s;
    }
}

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

Sentinel 控制台

Sentinel 控制台提供一个轻量级的控制台,它提供机器发现、单机资源实时监控、集群资源汇总,以及规则管理的功能。您只需要对应用进行简单的配置,就可以使用这些功能。

注意: 集群资源汇总仅支持 500 台以下的应用集群,有大概 1 - 2 秒的延时。

Sentinel 流量控制 熔断降级 初探

Figure 1. Sentinel Dashboard

开启该功能需要3个步骤:

获取控制台:地址 https://github.com/alibaba/Sentinel/releases

也可以从最新版本的源码自行构建 Sentinel 控制台:

  • 下载控制台工程

  • 使用以下命令将代码打包成一个 fat jar: mvn clean package

启动控制台

Sentinel 控制台是一个标准的 SpringBoot 应用,以 SpringBoot 的方式运行 jar 包即可。

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

如若8080端口冲突,可使用 -Dserver.port=新端口 进行设置。

配置控制台信息

application.yml

spring: cloud: sentinel: transport: port: 8719 dashboard: localhost:8080

这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了1个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。

客户端接入控制台

控制台启动后,客户端需要按照以下步骤接入到控制台。

引入JAR包

com.alibaba.csp sentinel-transport-simple-http 1.4.1

配置启动参数

启动时加入 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定控制台地址和端口。若启动多个应用,则需要通过 -Dcsp.sentinel.api.port=xxxx 指定客户端监控 API 的端口(默认是 8719)。

除了修改 JVM 参数,也可以通过配置文件取得同样的效果。

例如:

增加配置,在应用的 /src/main/resources/application.properties 中添加基本配置信息

spring.application.name=sentinel-example
server.port=18083
spring.cloud.sentinel.transport.dashboard=localhost:8080

优先级顺序:JVM -D 参数的优先级最高,若 properties 和 JVM 参数中有相同项的配置,以 JVM -D 参数配置的为准。

常见问题:

Sentinel Dashboard 是一个单独启动的控制台,应用若想上报监控信息给 Sentinel 控制台,需要引入 Sentinel 上报信息的客户端。它们各自有自己的通信端口,其中控制台的端口可通过启动参数 -Dserver.port=xxxx 进行配置,而 Sentinel 客户端的端口可以通过启动参数 -Dcsp.sentinel.api.port 进行配置(默认是 8719)。两者都启动之后,Sentinel 客户端在首次访问资源时会初始化并给控制台发送心跳,之后控制台会通过客户端提供的端口对 Sentinel 客户端进行访问来拉取相关信息。基于此,接入控制台需要的步骤如下:

  1. 接入 Sentinel 的应用应该引入 Sentinel 客户端通信的基础 jar 包,如 sentinel-transport-simple-http
  2. 客户端启动时添加相应的 JVM 参数,包括:
    • 应用名称:-Dproject.name=xxxx
    • 控制台地址:-Dcsp.sentinel.dashboard.server=ip:port
    • 本地的 Sentinel 客户端端口:-Dcsp.sentinel.api.port=xxxx(默认是 8719)
  3. 启动控制台,运行应用,当首次访问对应的资源后 等待一段时间即可在控制台上看到对应的应用以及相应的监控信息。可以通过 curl http://ip:port/tree 命令查看调用链,正常情况下会显示出已访问资源的调用链。

注意:Sentinel 会在客户端首次调用时候进行初始化,开始向控制台发送心跳包。因此需要确保客户端有访问量,才能在控制台上看到监控数据。另外,还是期待大家养成看日志的好习惯。

  • 控制台推送规则的日志默认位于 ${user.home}/logs/csp/sentinel-dashboard.log
  • 客户端接收规则日志默认位于 ${user.home}/logs/csp/sentinel-record.log.xxx

常用排查问题列表:

  • 确认 Dashboard 正常工作
  • 若是 Spring Boot / Dubbo 等应用,请务必检查是否引入了整合依赖并进行了相应配置
  • 检查客户端的启动参数配置是否正确
  • 通过日志排查客户端发送心跳包是否正常,是否正常上报给 Dashboard
  • 确保 fastjson 的版本和 Sentinel 的依赖版本保持一致
  • 通过 curl IP:port/getRules?type=flow 等命令查看结果,查看规则是否推送成功
  • 发送到客户端的规则格式是否正确,例如确认一下降级规则的表单是否填写完整
  • 某些不能访问互联网的坏境比如堡垒机可能导致前端文件无法下载也可能导致图出不来,可以浏览器调试查看到

触发客户端初始化

确保客户端有访问量,Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包。

feign 支持

Sentinel 适配了Feign组件。如果想使用,除了引入 sentinel-starter 的依赖外还需要 2 个步骤:

  • 配置文件打开 sentinel 对 feign 的支持:feign.sentinel.enabled=true

  • 加入 feign starter 依赖触发 sentinel starter 的配置类生效:

    org.springframework.cloud spring-cloud-starter-alibaba-sentinel 0.2.1.RELEASE org.springframework.cloud spring-cloud-starter-openfeign 2.0.2.RELEASE

这是一个 FeignClient 对应的接口:

package cn.chinotan.feign;

import cn.chinotan.config.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @program: test
 * @description: FeignClient 接口
 * @author: xingcheng
 * @create: 2019-01-12 18:43
 **/
@FeignClient(name = "echo-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class, url = "http://localhost:11111")
public interface EchoService {
    @RequestMapping(value = "/sentinel/show/echo/{str}", method = RequestMethod.GET)
    String echo(@PathVariable("str") String str);
}


package cn.chinotan.feign;

/**
 * @program: test
 * @description: EchoServiceFallback
 * @author: xingcheng
 * @create: 2019-01-12 18:44
 **/
public class EchoServiceFallback implements EchoService {
    @Override
    public String echo(String str) {
        return "echo fallback";
    }
}


package cn.chinotan.config;

import cn.chinotan.feign.EchoServiceFallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @program: test
 * @description: FeignConfiguration
 * @author: xingcheng
 * @create: 2019-01-12 18:44
 **/
@Configuration
public class FeignConfiguration {
    @Bean
    public EchoServiceFallback echoServiceFallback() {
        return new EchoServiceFallback();
    }
}

Feign 对应的接口中的资源名策略定义:httpmethod:protocol://requesturl

EchoService 接口中方法 echo 对应的资源名为 GET:http://localhost:11111/sentinel/show/echo/{str}。

即:

Sentinel 流量控制 熔断降级 初探

请注意:@FeignClient 注解中的所有属性,Sentinel 都做了兼容。

RestTemplate 支持

Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造 RestTemplate bean的时候需要加上 @SentinelRestTemplate 注解。

package cn.chinotan.config;

import cn.chinotan.exceptionUtil.ExceptionUtil;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @program: test
 * @description: restTemplate配置
 * @author: xingcheng
 * @create: 2019-01-12 18:47
 **/
@Configuration
public class RestConfiguration {

    @Bean
    @SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
}

@SentinelRestTemplate 注解的参数支持限流(blockHandlerblockHandlerClass)和降级(fallbackfallbackClass)的处理。

其中 blockHandler 或 fallback 对应的方法必须是对应 blockHandlerClass 或 fallbackClass中的静态方法。

该对应的方法参数跟 ClientHttpRequestInterceptor 接口中的参数一致,并多出了一个 BlockException 参数,且返回值是 ClientHttpResponse

比如上述 ExceptionUtil 的 handleException 方法对应的声明如下:

public class ExceptionUtil {
    public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) {
        ...
    }
}

@SentinelRestTemplate 注解的限流(blockHandlerblockHandlerClass)和降级(fallbackfallbackClass)不强制填写,当被 Sentinel 熔断后,会返回 RestTemplate request block by sentinel 信息,或者也可以编写对应的方法自行处理。

Sentinel RestTemplate 限流的资源规则提供两种粒度:

  • schema://host:port/path:协议、主机、端口和路径

  • schema://host:port:协议、主机和端口

以 [https://www.chinotan.cn/test](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.taobao.com%2Ftest) 这个 url 为例。对应的资源名有两种粒度,分别是 [https://www.chinotan.c](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.taobao.com%2F)n 以及 [https://](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.taobao.com%2Ftest)[www.chinotan.c](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.taobao.com%2F)n[/test](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.taobao.com%2Ftest)

动态数据源支持

在我们的第一个版本 0.2.0.RELEASE 或 0.1.0.RELEASE 中的使用方式如下

需要3个步骤才可完成数据源的配置:

  • 配置文件中定义数据源信息。比如使用文件:

application.properties

spring.cloud.sentinel.datasource.type=file
spring.cloud.sentinel.datasource.recommendRefreshMs=3000
spring.cloud.sentinel.datasource.bufSize=4056196
spring.cloud.sentinel.datasource.charset=utf-8
spring.cloud.sentinel.datasource.converter=flowConverter
spring.cloud.sentinel.datasource.file=/Users/you/yourrule.json

创建一个 Converter 类实现 com.alibaba.csp.sentinel.datasource.Converter 接口,并且需要在 ApplicationContext 中有该类的一个 Bean

@Component("flowConverter")
public class JsonFlowRuleListParser implements Converter<String, List<FlowRule>> {
    @Override
    public List<FlowRule> convert(String source) {
        return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
        });
    }
}

这个 Converter 的 bean name 需要跟 application.properties 配置文件中的 converter 配置一致

  • 在任意一个 Spring Bean 中定义一个被 @SentinelDataSource 注解修饰的 ReadableDataSource 属性

@SentinelDataSource("spring.cloud.sentinel.datasource") private ReadableDataSource dataSource;

@SentinelDataSource 注解的 value 属性表示数据源在 application.properties 配置文件中前缀。 该在例子中,前缀为 spring.cloud.sentinel.datasource 。

如果 ApplicationContext 中存在超过1个 ReadableDataSource bean,那么不会加载这些 ReadableDataSource 中的任意一个。 只有在 ApplicationContext 存在一个 ReadableDataSource 的情况下才会生效。

如果数据源生效并且规则成功加载,控制台会打印类似如下信息:

[Sentinel Starter] load 3 flow rules

后面的版本,重新进行了设计,使用方式如下

只需要1个步骤就可完成数据源的配置:

  • 直接在 application.properties 配置文件中配置数据源信息即可

比如配置了4个数据源:

spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json

spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json

spring.cloud.sentinel.datasource.ds3.zk.path = /Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW
spring.cloud.sentinel.datasource.ds3.zk.server-addr = localhost:2181

spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application
spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel
spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test

这样配置方式参考了 Spring Cloud Stream Binder 的配置,内部使用了 TreeMap 进行存储,comparator 为 String.CASE_INSENSITIVE_ORDER 。

d1, ds2, ds3, ds4 是 ReadableDataSource 的名字,可随意编写。后面的 file ,zknacos , apollo 就是对应具体的数据源。 它们后面的配置就是这些数据源各自的配置。

每种数据源都有两个共同的配置项: data-type 和 converter-class 。

data-type 配置项表示 Converter,Spring Cloud Alibaba Sentinel 默认提供两种内置的值,分别是 json 和 xml (不填默认是json)。 如果不想使用内置的 json 或 xml 这两种 Converter,可以填写 custom 表示自定义 Converter,然后再配置 converter-class 配置项,该配置项需要写类的全路径名。

这两种内置的 Converter 只支持解析 json 数组 或 xml 数组。内部解析的时候会自动判断每个 json 对象或xml对象属于哪4种 Sentinel 规则(FlowRuleDegradeRuleSystemRuleAuthorityRule)。

比如10个规则数组里解析出5个限流规则和5个降级规则。 这种情况下该数据源不会注册,日志里页会进行警告。

如果10个规则里有9个限流规则,1个解析报错了。这种情况下日志会警告有个规则格式错误,另外9个限流规则会注册上去。

这里 json 或 xml 解析用的是 jacksonObjectMapper 或 XmlMapper 使用默认的配置,遇到不认识的字段会解析报错。 因为不这样做的话限流 json 也会解析成 系统规则(系统规则所有的配置都有默认值),所以需要这样严格解析。

当然还有一种情况是 json 对象或 xml 对象可能会匹配上所有4种规则(比如json对象里只配了 resource 字段,那么会匹配上4种规则),这种情况下日志里会警告,并且过滤掉这个对象。

用户使用这种配置的时候只需要填写正确的json或xml就行,有任何不合理的信息都会在日志里打印出来。

如果数据源生效并且规则成功加载,控制台会打印类似如下信息:

[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule
[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule

默认情况下,xml 格式是不支持的。需要添加 jackson-dataformat-xml 依赖后才会自动生效。

Endpoint 支持

在使用 Endpoint 特性之前需要在 Maven 中添加 spring-boot-starter-actuator 依赖,并在配置中允许 Endpoints 的访问。

  • Spring Boot 1.x 中添加配置 management.security.enabled=false。暴露的 endpoint 路径为 /sentinel

  • Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*。暴露的 endpoint 路径为 /actuator/sentinel

例如:

Sentinel 流量控制 熔断降级 初探

最后测试一把:

控制器层为:

package cn.chinotan.controller;

import cn.chinotan.feign.EchoService;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @program: test
 * @description: 最简单的使用 Sentinel 的例子
 * @author: xingcheng
 * @create: 2019-01-12 18:01
 **/
@RestController
@RequestMapping("/sentinel")
public class SentinelController {

    @Autowired
    EchoService echoService;
    
    @Autowired
    RestTemplate restTemplate;

    @GetMapping(value = "/hello")
    @SentinelResource(value = "hello", fallback = "helloFallback")
    public String hello() {
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Hello Sentinel";
    }

    public String helloFallback() {
        return "helloFallback";
    }

    @GetMapping(value = "/echoFeign/{str}")
    public String echoFeign(@PathVariable("str") String str) {
        System.out.println("echoFeign");
        return echoService.echo(str);
    }

    @GetMapping(value = "/echoRestTemplate/{str}")
    public String echoRestTemplate(@PathVariable("str") String str) {
        System.out.println("echoRestTemplate");
        return restTemplate.getForObject("http://localhost:11111/sentinel/show/echo/" + str, String.class);
    }

    @GetMapping(value = "/show/echo/{str}")
    public String echo(@PathVariable("str") String str) {
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("show_echo");
        return "show_echo: " + str;
    }
}

降级规则为:

Sentinel 流量控制 熔断降级 初探

降级策略

我们通常用以下几种方式来衡量资源是否处于稳定的状态:

  • 平均响应时间 (DEGRADE_GRADE_RT):当资源的平均响应时间超过阈值(DegradeRule 中的 count,以 ms 为单位)之后,资源进入准降级状态。接下来如果持续进入 5 个请求,它们的 RT 都持续超过这个阈值,那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回(抛出 DegradeException)。
  • 异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。

注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。

降级结果:

Sentinel 流量控制 熔断降级 初探

Sentinel 流量控制 熔断降级 初探

More

下表显示当应用的 ApplicationContext 中存在对应的Bean的类型时,会进行的一些操作:

存在Bean的类型

操作

作用

UrlCleaner

WebCallbackManager.setUrlCleaner(urlCleaner)

资源清理(资源(比如将满足 /foo/:id 的 URL 都归到 /foo/* 资源下))

UrlBlockHandler

WebCallbackManager.setUrlBlockHandler(urlBlockHandler)

自定义限流处理逻辑

下表显示 Spring Cloud Alibaba Sentinel 的所有配置信息:

配置项

含义

默认值

spring.cloud.sentinel.enabled

Sentinel自动化配置是否生效

true

spring.cloud.sentinel.eager

取消Sentinel控制台懒加载

false

spring.cloud.sentinel.transport.port

应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer

8721

spring.cloud.sentinel.transport.dashboard

Sentinel 控制台地址

 

spring.cloud.sentinel.transport.heartbeatIntervalMs

应用与Sentinel控制台的心跳间隔时间

 

spring.cloud.sentinel.filter.order

Servlet Filter的加载顺序。Starter内部会构造这个filter

Integer.MIN_VALUE

spring.cloud.sentinel.filter.spring.url-patterns

数据类型是数组。表示Servlet Filter的url pattern集合

/*

spring.cloud.sentinel.metric.charset

metric文件字符集

UTF-8

spring.cloud.sentinel.metric.fileSingleSize

Sentinel metric 单个文件的大小

 

spring.cloud.sentinel.metric.fileTotalCount

Sentinel metric 总文件数量

 

spring.cloud.sentinel.log.dir

Sentinel 日志文件所在的目录

 

spring.cloud.sentinel.log.switch-pid

Sentinel 日志文件名是否需要带上pid

false

spring.cloud.sentinel.servlet.blockPage

自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL

 

spring.cloud.sentinel.flow.coldFactor

冷启动因子

3

参考:https://github.com/alibaba/Sentinel/wiki

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这