6.Sentinel

Sentinel:分布式系统的流量防卫兵

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

Sentinel 具有以下特征:

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

Sentinel分为两个部分:

  • 核心库(java 客户端)不依赖任何框架/库,能够运行于所有Java运行时环境,同时对Dubbo/Spring Cloud等框架也有较好的支持。每个微服务客户端都需要整合sentinel的客户端封装与配置,才能将监控信息上报给dashboard展示以及实时的更改限流或熔断规则等。
  • 控制台(Dashboard)基于Spring Boot开发 打包后可以直接运行,不需要额外的Tomcat等应用容器。提供了流控规则、熔断规则的在线维护等功能
安装并启动控制台

1.从以下地址下载jar包
https://github.com/alibaba/Sentinel/releases
2.启动控制台,默认8080端口,账号密码默认sentinel
在这里插入图片描述
在这里插入图片描述

服务引入Sentinel核心库

1.pom引入依赖

	<!-- SpringCloud alibaba sentinel -->
	<dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
     </dependency>
    <!-- SpringCloud alibaba sentinel-datasource-nacos 后续做持久化用到 -->
	<dependency>
	      <groupId>com.alibaba.csp</groupId>
	      <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>

2.添加pom配置

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-8401
  cloud:
    #nacos相关配置
    nacos:
      discovery:
        server-addr: localhost:8848
    #sentinel 相关配置
    sentinel:
      transport:
        #默认8719端口,如果被占用会从8719+1开始依次扫描 直到找到未占用的端口 该端口是在本机启动个http service端口与控制台做交互
        port: 8719
        #配置 sentinel 监控平台地址
        dashboard: localhost:8080
management:
  endpoints:
    web:
      exposure:
        include: '*'

3.创建测试接口,并且访问后登陆监控平台查看。因为sentinel使用的是懒加载,所以必须访问一次才可以从监控平台看到
在这里插入图片描述

流控

在这里插入图片描述
基本介绍

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

直接模式 QPS
表示1秒钟内查询一次ok,超过一次失败并报默认错误
在这里插入图片描述
在这里插入图片描述

直接模式 线程
线程限流和QPS的限流有区别,QPS被挡在了应用服务外,而线程限流可以进入应用内,当应用中的线程数高于单机阈值才会触发限流。例如此时一秒内点击多次测试接口,不会触发限流。但是加大测试接口的执行时间Thread.sleep(800);,同时一秒内点击多次测试接口就会出现限流,而用QPS不需要加大执行时间,只要一秒钟内请求数达到阈值就会限流。
在这里插入图片描述

关联模式
关联模式依旧是针对/testA的限流,但是触发对象不是自己而是关联对象/testB,当testB的QPS达到了阈值,那么testA就会触发限流。在实际应用场景中,比如支付服务达到了阈值,订单服务进行限流。
在这里插入图片描述

链路模式
针对访问testService/testA接口限流
在这里插入图片描述
创建service

@Service
public class TestService {

    @SentinelResource("testService")
    public String testService(){
        return "testService";
    }
}

/testAtestB接口同时调用该方法,按照以上配置会触发限流。但是因为版本问题,链路限流可能不生效,可以通过一下两种方法解决
方法1:
配置文件修改

spring.cloud.sentinel.web-context-unify=false

方法2:
重写配置

@Configuration
public class FilterContextConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new CommonFilter());
        registrationBean.addUrlPatterns("/*");
        // 入口资源关闭聚合
        registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
        registrationBean.setName("sentinelFilter");
        registrationBean.setOrder(1);

        return registrationBean;
    }
}


预热效果
默认coldFactor为3,即请求QPS从(threshold / 3) 开始,经过多长预热时长才逐渐升值设定的QPS阈值。
系统初始化阈值为10 /3约等于3,既阈值刚开始为3;然后过了5秒后阈值慢慢恢复到10
应用场景:例如秒杀系统在开启的瞬间,会有很多流量,很可能直接把系统打死,预热方式就是为了保护系统,可以慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。
在这里插入图片描述

匀速效果
匀速排队方式会严格控制请求通过的间隔时间,对应的是漏牌桶算法。这种方式主要处理间隔性突发的流量,例如消息队列。在某一秒有大量请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲时间逐渐处理这些请求,而不是在第一时间直接拒绝多余的请求。
在这里插入图片描述

熔断

一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等,然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高)对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都会自动熔断(默认行为是抛出DegradeException)。

Sentinel 提供以下几种熔断策略:

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

在这里插入图片描述
慢调用
在2000毫秒内(统计时长),请求数大于等于2(最小请求数),且失败比例50%以上(比例阈值)慢调用(RT200毫秒)就熔断5秒(熔断时长),5秒后进入探测恢复状态
在这里插入图片描述

传入参数1测试接口暂停300毫秒执行,请求一次后断路器打开,第二次服务不可用
在这里插入图片描述
5秒后试探性恢复,断路器关闭,服务恢复
在这里插入图片描述
在这里插入图片描述
异常比例和异常数的调用和慢调用差不多,一个是统计的异常比例,一个是异常数,都没有RT
在这里插入图片描述
在这里插入图片描述

热点流控

热点就是经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
在这里插入图片描述
在这里插入图片描述

添加测试代码,测试过程中发现不使用@SentinelResource注解不生效,该注解处理的是Sentinel配置的违规后的情况,不处理Java运行时异常

	@GetMapping("/testHopKey")
    @SentinelResource(value = "testHopKey",blockHandler = "dealHandler_testHopKey")
    public String testHopKey(@RequestParam(value = "a",required = false) String a,
                             @RequestParam(value = "b",required = false)String b) {

        return "------hopKey";
    }
    public String dealHandler_testHopKey(String a, String b, BlockException e){
        return "------dealHandler_testHopKey";
    }

编辑热点规则,当1秒内的QPS大于1就会触发流控,会走入dealHandler_testHopKey方法。其中参数索引是热点参数的位置,第三个url因为只有参数b没有索引为0的参数a,所以不会走入限流的
限流:http://localhost:8401/testHopKey?a=1
限流:http://localhost:8401/testHopKey?a=1&b=2
通过:http://localhost:8401/testHopKey?b=2
在这里插入图片描述

在这里插入图片描述
参数例外项
是指当参数a不等于5的时候,阈值为1;当参数a等于5的时候阈值为200
在这里插入图片描述

系统规则

系统规则的控制粒度更加广,是入口级别的,具体看官网配置
https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
在这里插入图片描述

SentinelResource

Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。
在上面的热点key限流中使用了该注解,不过是方法级的,如果每个方法都这样添加一个对应的限流异常方法,既提高耦合度又会使代码膨胀还不是全局的兜底机制,所以可以使用blockHandlerblockHandlerClass来构建全局限流设置,fallbackfallbackClass同理。

使用了@SentinelResource后,规则配置中必须是@SentinelResource的value值,@SentinelResource才会生效,配置后当走入控制台配置的限流、熔断规则/出现异常 时候就会走如blockHandler/fallback指定的方法内。如果两个都配置会优先走入blockHandler内。

public class ConsumerHandler {

    public static CommonResult handleException1(BlockException e){
        return new CommonResult(4444,"服务不可用--2",null);
    }

    public static CommonResult handleException2(BlockException e){
        return new CommonResult(4444,"服务不可用--1",null);
    }
}

之后在方法上添加@SentinelResource注解时,直接指定上面的类和对应方法即可,不必一个为每个方法建立一个限流方法

 @SentinelResource(value = "consumerBlockHandler",blockHandlerClass = ConsumerHandler.class,blockHandler = "handleException2")

@SentinelResource 注解包含以下属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT)
  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
    1.返回值类型必须与原函数返回值类型一致;
    2.方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    3.fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所以类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    1.返回值类型必须与原函数返回值类型一致;
    2.方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    3.defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
    若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出。

控制台添加规则的方式是最原始的,且是存在于内存中的,官方并不推荐使用,建议规则持久化注册中心。

两种方式:
1.如果想要实现控制台添加自动存入Nacos需要在源码的基础上改造控制台
2.以下方式

规则持久化

一旦重启应用,sentinel的规则将消失,生产环境需要将配置规则进行持久化。

将限流配置规则持久化进Nacos保存,只要刷新应用的某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,sentinel上的流控规则就持续有效。
1.添加配置

		<dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

2.服务配置文件中sentinel指定Nacos地址

spring:
	cloud:
		sentinel:
			datasource:
					#数据源1  这个名字任意起
			        dsr1:
			          nacos:
			          	#nacos配置
			            server-addr: localhost:8848
			            dataId: cloudalibaba-sentinel-8401
			            groupId: DEFAULT_GROUP
			            data-type: json
			            # 规则类型:流控
			            rule-type: flow

3.Nacos配置流控规则
在这里插入图片描述

[
  {
    "resource": "/testB",
    "controlBehavior": 0,
    "count": 1,
    "grade": 1,
    "limitApp": "default",
    "strategy": 0,
    "clusterMode": false
  }
]

resource:资源名称
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
count:单机阈值
grade:阈值类型 0表示线程数 ,1表示QPS
limitApp:来源应用
strategy:流控模式,0表示直接,1表示关联,2表示链路
clusterMode:是否集群
4.重启应用,然后访问就可以看到Sentinel添加后的流控规则

不同的规则对应不同的配置

流控规则

[
  {
    // 资源名
    "resource": "/test",
    // 针对来源,若为 default 则不区分调用来源
    "limitApp": "default",
    // 限流阈值类型(1:QPS;0:并发线程数)
    "grade": 1,
    // 阈值
    "count": 1,
    // 是否是集群模式
    "clusterMode": false,
    // 流控效果(0:快速失败;1:Warm Up(预热模式);2:排队等待)
    "controlBehavior": 0,
    // 流控模式(0:直接;1:关联;2:链路)
    "strategy": 0,
    // 预热时间(秒,预热模式需要此参数)
    "warmUpPeriodSec": 10,
    // 超时时间(排队等待模式需要此参数)
    "maxQueueingTimeMs": 500,
    // 关联资源、入口资源(关联、链路模式)
    "refResource": "rrr"
  }

权限规则

[
  {
    // 资源名
    "resource": "sentinel_spring_web_context",
    // 流控应用
    "limitApp": "/test",
    // 授权类型(0代表白名单;1代表黑名单。)
    "strategy": 0
  }
]

降级规则

[
  {
      // 资源名
    "resource": "/test1",
    "limitApp": "default",
    // 熔断策略(0:慢调用比例,1:异常比率,2:异常计数)
    "grade": 0,
    // 最大RT、比例阈值、异常数
    "count": 200,
    // 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
    "slowRatioThreshold": 0.2,
    // 最小请求数
    "minRequestAmount": 5,
    // 当单位统计时长(类中默认1000)
    "statIntervalMs": 1000,
    // 熔断时长
    "timeWindow": 10
  }
]

系统规则

[
    {
      // RT
      "avgRt": 1,
      // CPU 使用率
      "highestCpuUsage": -1,
      // LOAD
      "highestSystemLoad": -1,
      // 线程数
      "maxThread": -1,
      // 入口 QPS
      "qps": -1
    }
  ]

授权规则

[
  {
    // 资源名
    "resource": "sentinel_spring_web_context",
    // 流控应用
    "limitApp": "/test",
    // 授权类型(0代表白名单;1代表黑名单。)
    "strategy": 0
  }
]

热点参数规则

[
  {
    // 资源名
    "resource": "/test1",
    // 限流模式(QPS 模式,不可更改)
    "grade": 1,
    // 参数索引
    "paramIdx": 0,
    // 单机阈值
    "count": 13,
    // 统计窗口时长
    "durationInSec": 6,
    // 是否集群 默认false
    "clusterMode": false,
    //
    "burstCount": 0,
    // 集群模式配置
    "clusterConfig": {
      //
      "fallbackToLocalWhenFail": true,
      //
      "flowId": 2,
      //
      "sampleCount": 10,
      //
      "thresholdType": 0,
      //
      "windowIntervalMs": 1000
    },
    // 流控效果(支持快速失败和匀速排队模式)
    "controlBehavior": 0,
    //
    "limitApp": "default",
    //
    "maxQueueingTimeMs": 0,
    // 高级选项
    "paramFlowItemList": [
      {
        // 参数类型
        "classType": "int",
        // 限流阈值
        "count": 222,
        // 参数值
        "object": "2"
      }
    ]
  }
]

yml文件配置

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos 服务地址
    sentinel:
      eager: true   #饿加载
      transport:
        dashboard: localhost:8089 #配置 Sentinel dashboard 地址
      datasource:
        flow:   #流控规则
          nacos:
            namespace: 1efe62b0-b759-45ff-a5e7-4fb5c27350c7
            server-addr: localhost:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: flow
        degrade:
          nacos:
            namespace: 1efe62b0-b759-45ff-a5e7-4fb5c27350c7
            server-addr: localhost:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: degrade
        system:
          nacos:
            namespace: 1efe62b0-b759-45ff-a5e7-4fb5c27350c7
            server-addr: localhost:8848
            dataId: ${spring.application.name}-system-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: system
        authority:
          nacos:
            namespace: 1efe62b0-b759-45ff-a5e7-4fb5c27350c7
            server-addr: localhost:8848
            dataId: ${spring.application.name}-authority-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: authority
        param-flow:
          nacos:
            namespace: 1efe62b0-b759-45ff-a5e7-4fb5c27350c7
            server-addr: localhost:8848
            dataId: ${spring.application.name}-param-flow-rules
            groupId: SENTINEL_GROUP
            data-type: json
            rule-type: param-flow
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值