一、Sentinel简介

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

1、sentinel的特征

  • 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等
  • 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况
  • 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
    -完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等

2、sentinel的主要特性

微服务状态管理 微服务 sentinel_微服务


sentinel的开源生态

微服务状态管理 微服务 sentinel_微服务状态管理_02


sentinel 的架构图

微服务状态管理 微服务 sentinel_spring cloud_03


3、sentinel的主要优势和特性

  • 轻量级,核心库无多余依赖,性能损耗小
  • 方便接入,开源生态广泛
  • 丰富的流量控制场景
  • 易用的控制台,提供实时监控、机器发现、规则管理等能力
  • 完善的扩展性设计,提供多样化的 SPI 接口,方便用户根据需求给 Sentinel 添加自定义的逻辑

4、sentinel与spring cloud Hystrix 对比

Sentinel

Hystrix

隔离策略

信号量隔离

线程池隔离/信号量隔离

熔断降级策略

基于响应时间或失败比率

基于失败比率

实时指标实现

滑动窗口

滑动窗口(基于 RxJava)

规则配置

支持多种数据源

支持多种数据源

扩展性

多个扩展点

插件的形式

基于注解的支持

支持

支持

限流

基于 QPS,支持基于调用关系的限流

有限的支持

流量整形

支持慢启动、匀速器模式

不支持

系统负载保护

支持

不支持

控制台

开箱即用,可配置规则、查看秒级监控、机器发现等

不完善

常见框架的适配

Servlet、Spring Cloud、Dubbo、gRPC 等

Servlet、Spring Cloud Netflix

5、sentinel分为两个部分

  • 核心库(Java 客户端): 不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持
  • 控制台(Dashboard): 基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器

二、Sentinel安装与使用

1、Sentinel控制台的下载
下载地址:https://github.com/alibaba/Sentinel/releases/tag/1.8.3

微服务状态管理 微服务 sentinel_微服务_04


我们选择1.8.3版本,直接下载jar包

微服务状态管理 微服务 sentinel_微服务_05

放到指定文件夹中

微服务状态管理 微服务 sentinel_spring cloud_06


2、Sentinel控制台的启动

cmd 启动jar包

java -jar sentinel-dashboard-1.8.3.jar

微服务状态管理 微服务 sentinel_微服务_07


微服务状态管理 微服务 sentinel_微服务_08


2、访问浏览器输入:localhost:8080

账号密码 默认都是 sentinel

微服务状态管理 微服务 sentinel_微服务状态管理_09


微服务状态管理 微服务 sentinel_微服务状态管理_10


3、为服务打开sentinel的监控

引入sentinel依赖

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
        </dependency>


手把手教你搭建springcloud alibaba微服务–openfeign

yml文件添加sentinel相关配置

server:
  port: 9090

spring:
  application:
    name: mdx-shop-user
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: mdx
        group: mdx
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
feign:
  sentinel:
    enabled: true

启动user服务并访问user服务的测试接口

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("getOrderNo")
    public String getOrderNo(String userId,String tenantId,HttpServletRequest request){
        return userService.getOrderNo(userId,tenantId,request);
    }
}

浏览器访问
http://localhost:9090/user/getOrderNo?userId=mdx123456&tenantId=123

微服务状态管理 微服务 sentinel_限流_11


然后查看sentinel控制台发现已经监控到了我们服务的接口

微服务状态管理 微服务 sentinel_微服务状态管理_12

三、Sentinel的控制规则

1、流控规则

1.1、流控规则的各个属性

  • 资源名: 唯一名称,默认请求路径,表示对该资源进行流控
  • 针对来源: Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单击阈值:
    QPS:(每秒钟的请求数量):当调用该api的QPS达到阈值时,进行限流
    线程数:当调用该线程数达到阈值的时候,进行限流
  • 是否集群:不需要集群
  • 流控模式:
    直接: api达到限流条件时,直接限流
    关联: 当关联的资源达到阈值时,就限流自己
    链路: 只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
  • 流控效果:
    快速失败: 直接失败,抛异常
    Warm Up: 根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFctor,经过预热时长,才达到设置的QPS阈值
    排队等待: 匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

1.2、新增流控规则

微服务状态管理 微服务 sentinel_限流_13

资源名称就是我们的接口访问路径

然后我们一秒一次访问一下接口(正常)

http://localhost:9090/user/getOrderNo?userId=mdx123456&tenantId=123 可以看到正常返回数据

微服务状态管理 微服务 sentinel_限流_14

接下来我们快速请求接口,一秒钟点击多次

发现已经被限流了

微服务状态管理 微服务 sentinel_微服务状态管理_15

1.3、流控规则–关联流控模式

我们模拟一下流控模式中的关联模式
关联: 当关联的资源达到阈值时,就限流自己,也就是说关联的资源(接口),QPS为1时,一秒内被多次请求的时候,自己的接口就会被限流。

我们在user服务中创建一个关联接口 /user/sentinelB

@GetMapping("sentinelB")
    public String sentinelB(){
        return "我是关联接口";
    }

然后修改一下流控规则

微服务状态管理 微服务 sentinel_限流_16


我们用jmeter模拟一下请求sentinelB接口

一秒3个请求并循环

微服务状态管理 微服务 sentinel_微服务_17


在这期间我们访问/user/getOrderNo接口

发现被限流了

微服务状态管理 微服务 sentinel_sentinel_18

2、@SentinelResource注解的使用

2.1、@SentinelResource的属性

  • value:作用指定资源名称,必填
  • entryType:entry类型,标记流量的方向,指明是出口流量,还是入口流量;取值 IN/OUT ,默认是OUT。非必填
  • blockHandler:处理BlockException的函数名称,函数要求为
    必须是public
    返回类型与原方法一致
    参数类型需要和原方法相匹配,并在最后加上BlockException类型的参数
    默认需和原方法在同一个类中,如果希望使用其他类的函数,可配置blockHandlerClass,并指定blockHandlerClass里面的方法
  • blockHandlerClass:存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析。函数要求为:
    必须是public
    返回类型与原方法一致
    参数类型需要和原方法相匹配,并在最后加上BlockException类型的参数
  • fallback:用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了execptionsToIgnore 里面排除掉的异常类型)进行处理,函数要求为:
    返回类型与原方法一致
    参数类型需要和原方法相匹配,Sentinel 1.6版本之后,也可在方法最后加上Throwable类型的参数
    默认需和原方法在同一个类中,若希望使用其他类的函数,可配置fallbackClass,并指定fallbackClass里面的方法
  • fallbackClass:存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。
  • defaultFallback:用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:
    返回类型与原方法一致
    方法参数列表为空,或者有一个Throwable类型的参数
    默认需要和原方法在同一个类中,若希望使用其他类的函数,可配置fallbackclass,并指定fallbackClass里面的方法。
  • exceptionsToIgnore:指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出
  • exceptionsToTrace:需要trace的异常

2.2、按照资源名称的方式限流,并对限流流进行友好处理
我们修改一下/user/getOrderNo接口的代码,增加@SentinelResource注解,并做后续处理
演示代码:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("getOrderNo")
    @SentinelResource(value = "getOrderNoResource",blockHandler = "getOrderNoBlockHandler",blockHandlerClass = UserController.class)
    public String getOrderNo(String userId, String tenantId, HttpServletRequest request){
        return userService.getOrderNo(userId,tenantId,request);
    }


    /**
     * 限流后续操作方法
     * @param e
     * @return
     */
    public static String getOrderNoBlockHandler(String userId, String tenantId, HttpServletRequest request,BlockException e){
        String msg = "不好意思,前方拥挤,请您稍后再试";
        return msg;
    }

}

其中 getOrderNoResource 为资源名称,getOrderNoBlockHandler 为兜底方法的方法名称,blockHandlerClass = UserController.class 这个是兜底方法所在的类, 与资源方法在同一个类中 这属性可以省去,这里只是演示
注意:返回类型与原方法一致,参数类型需要和原方法相匹配,并在最后加上BlockException类型的参数

然后再修改一下流控规则,把资源名称换成我们注解中的 getOrderNoResource

微服务状态管理 微服务 sentinel_微服务状态管理_19


快速访问我们的接口,http://localhost:9090/user/getOrderNo?userId=mdx123456&tenantId=123

发现返回的信息已经变成我们兜底方法自定义的返回提示了

微服务状态管理 微服务 sentinel_限流_20

3、熔断规则

3.1、熔断规则的几种策略

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断

3.2、新建一个熔断规则
我们先写一个新的接口并且睡眠一秒,使用注解资源名称的形式,并创建自定义兜底方法
方法接口地址 /user/sentinelA

@GetMapping("/sentinelA")
    @SentinelResource(value = "sentinelAResource" , fallback = "sentinelAResource", fallbackClass = UserSentinelResourceHandler.class)
    public String sentinelA(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("我是sentinelA");
        return "我是sentinelA";
    }

创建单独的handler类来处理兜底方法

@Component
public class UserSentinelResourceHandler {

    public static String sentinelAResource(Throwable throwable){
    	System.out.println("触发熔断,服务不可用");
        return "触发熔断,服务不可用";
    }
}

接着我们在控制台新建一个熔断规则(使用慢调用比例的策略)

微服务状态管理 微服务 sentinel_spring cloud_21

我们对以上添加的熔断规则解释一下:
在 2 秒的统计时间(统计时长)内,对资源接口的请求数大于 5 (最小请求数),请求响应时间(最大 RT)大于200毫秒的请求占比超过比例阈值 0.1 ,则下一个请求开始触发熔断,熔断时长是 5 秒,也就是 5 秒内,熔断器是打开状态,这期间的请求都会触发熔断,5秒过后熔断器关闭,此时熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
RT(平均响应时间): 当1s内持续进入5个请求,且对应请求的平均响应时间(秒级)均超过阈值,那么在接下来的时间窗口期内,对该方法的调用都会自动的熔断。注意Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要更改上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置

然后我们测试一下

模拟下并发,一秒十个请求循环调用

微服务状态管理 微服务 sentinel_微服务状态管理_22


我们发现触发阈值之后 已经触发了熔断机制,如下

微服务状态管理 微服务 sentinel_sentinel_23

3、热点规则

热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的数据,并对其访问进行限制,比如对某个商品id进行限制,或者对某个用户id进行限制

3.1、热点规则属性
参数索引:方法中参数的索引第几个参数
单机阈值:每秒达到单机阈值的数量就会触发兜底方法

4.1、新建一个热点规则
我们先创建一个测试方法,使用注解资源名称的形式,并创建自定义兜底方法

/**
     * 测试centinel热点规则限流
     * @param userId
     * @param shopId
     * @return
     */
    @GetMapping("/hotspot")
    @SentinelResource(value = "hotspotResource" , blockHandler = "hotspotResource", blockHandlerClass = UserSentinelResourceHandler.class)
    public String hotspot(@RequestParam(value = "userId" ,required = false) String userId,
                          @RequestParam(value = "shopId" ,required = false) String shopId){
        System.out.println("我是hotspot");
        return "我是hotspot";
    }

兜底方法

public static String hotspotResource(String userId, String shopId,BlockException blockException){
        System.out.println("您被认为恶意访问,触发热点限流");
        return "您被认为恶意访问,触发热点限流";
    }

然后我们在控制台新建热点规则

微服务状态管理 微服务 sentinel_限流_24


其中参数索引 0 代表的就是userId这个参数

我们访问一下接口

先访问带shopId的(一秒内多次访问) http://localhost:9090/user/hotspot?shopId=4

正常返回

微服务状态管理 微服务 sentinel_sentinel_25


再访问下带userId参数的(一秒内多次访问) http://localhost:9090/user/hotspot?userId=4

返回降级函数的内容

微服务状态管理 微服务 sentinel_spring cloud_26

4、系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性

如果配置了系统规则,所有的接口都会被限流

这里系统规则就不演示了,关于sentinel还有很多规则,策略和属性,大家有兴趣可以自行研究下。

关于sentinel的使用篇就到这里了。