springcloud alibaba之sentinel流量控制、熔断降级、系统负载

springcloud学习笔记,第十三章,Sentinel流量控制、熔断降级、系统负载

简介:

Sentinel哨兵=Hystrix豪猪

Sentinel 是什么?

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

Sentinel 具有以下特征:

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

Sentinel 的主要特性:

Sentinel 的开源生态:

安装sentinel:

sentinel由两部分组成:

  • 后台和前台8080

Sentinel 的使用可以分为两个部分:

  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持(见 主流框架适配)。
  • 控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等。

安装:java -jar sentinel-dashboard-1.8.0.jar

访问:localhost:8080,登录账户和密码都是sentinel。

微服务整合Sentinel:

  1. 启动nacos8848成功

  2. 新建moudle,cloudalibaba-sentinel-service8401

  3. POM:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!--SpringCloud ailibaba nacos -->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
    <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel -->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!--openfeign-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  4. YML:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    server:
    port: 8401

    spring:
    application:
    name: cloudalibaba-sentinel-service
    cloud:
    nacos:
    discovery:
    server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
    transport:
    dashboard: localhost:8080 #配置Sentinel dashboard地址
    port: 8719

    management:
    endpoints:
    web:
    exposure:
    include: '*'
  5. 主启动:

    1
    2
    @EnableDiscoveryClient
    @SpringBootApplication
  6. 业务类FlowLimitController:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @GetMapping("/testA")
    public String testA()
    {
    return "------testA";
    }
    @GetMapping("/testB")
    public String testB()
    {
    log.info(Thread.currentThread().getName()+"\t"+"...testB");
    return "------testB";
    }
  7. 启动sentinel:

  8. 启动微服务8401

  9. 启动8401微服务后查看sentinel控制台

    • sentinel采用的是懒加载模式,需要你执行一次访问即可。

Sentinel的流控规则:

简介:

流控模式:

  1. 直接模式,api达到限流条件式时,就直接限流。
  2. 关联
  3. 链路

QPS-直接-快速失败:

QPS:每秒请求数。

系统默认的就是直接快速失败的模式:这里的单机阈值1就是1秒钟1次。

测试:

  • 快速点击访问localhost:8401/testA,结果就是Blocked by Sentinel(flow limiting),服务限流了。

线程数-直接-快速失败:

QPS和线程数的区别:请求需要包装在线程里面运行。一个线程从请求开始到请求完成的生命周期里要有时间限制的。

如果在controller里面设置:一个线程睡眠几秒钟。那么就会出现一个窗口处理多个线程的情况,(阈值是一的话)就会出现失败。

关联:

当关联的资源达到阈值时,就限流自己。

设置效果:当关联资源/testB的qps阈值超过1时,就限流/testA的rest访问地址,当关联资源到阈值后限制配置好的资源名。

这样运行的结果就是,当模拟并发密集访问testB,运行后会发现testA也挂了。

链路:

自己模拟去吧。

流控效果:

直接失败:

前面已经模拟过了。

预热warm up:

Warm Up:

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过”冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo

公式:

阈值除以coldFactor(冷加载因子,默认是3),经过预热时长后才会达到阈值。

例:

解释上图:单机阈值是10表示我们一开始设想的是系统的最大并发是10个请求。但是使用warm up流控效果,所以就是10/3(冷加载因子,默认是3)=3,那么就是一开始只让3个并发量,后续再经过5秒的预热时长,慢慢加载到10。

冷加载因子:

应用场景:

如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能会把系统打死。预热方式就是为了保护系统,可以慢慢的把流量放进来,慢慢的把阈值增长到设定的阈值。

排队等待:

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo

该方式的作用如下图所示:

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

sentinel降级规则:

简介:

降级策略:1.RT,2.异常比例,3.异常数。

Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都会自动熔断(默认行为是抛出DegradeException)

注意:sentinel的断路器是没有半开状态的!

回顾下hystrix半开状态:

RT:

我用的sentinel版本是:1.8.0,RT变为了“慢调用比例”

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

测试:

  1. 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping("/testD")
    public String testD()
    {
    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
    log.info("testD 测试RT");

    return "------testD";
    }
  2. 配置:

    表示200毫秒处理一个请求,下一个时间窗口是1秒后。

  3. jmeter压测:

    一秒10个请求。

  4. 结论:

    因为1秒10个请求,那么一个请求就是100毫秒。所以同时满足默认一秒5个线程和处理一个请求时间的双重限制。触发降级。

异常比例:

  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

测试:

  1. 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping("/testD")
    public String testD()
    {

    log.info("testD 测试RT");
    int age = 10/0;
    return "------testD";
    }
  2. 配置:

    要求请求的错误率最大为0.2(20%)。

  3. jmeter:1秒10次请求。

  4. 结论:

    因为我们的请求就是100%报错,并且每秒请求数大于10次 。所以就会触发降级。

异常数:

  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

测试:

  1. 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping("/testE")
    public String testE()
    {
    log.info("testE 测试异常数");
    int age = 10/0;
    return "------testE 测试异常数";
    }

  2. 配置:

  3. 结论:

    访问localhost:8401/testE我们可以看到,第一次访问绝对报错,因为除数不能为0,我们可以看到error窗口,但是达到5次报错之后,进入熔断后降级。

Sentinel热点规则:

简介:

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

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

之前我们在hytrix中学习过了兜底方法:

定义热点规则:

  1. 代码

    deal_testHotKey()方法就是兜底方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
    @RequestParam(value = "p2",required = false) String p2) {
    //int age = 10/0;
    return "------testHotKey";
    }

    //兜底方法
    public String deal_testHotKey (String p1, String p2, BlockException exception){
    return "------deal_testHotKey,o(╥﹏╥)o";
    }
  2. 配置:

    上图的解释就是:参数索引就会请求传入参数的索引(默认是从0开始),此时我们访问/testHotkey并且带上p1参数,才能触发降级方法。方法testHotKey里面第一个参数只要QPS超过每秒1次。马上降级处理。

  3. 测试:

热点规则中的其他选项:

注意:

Sentinel系统规则:

系统自适应限流:从整体维度对应用入口进行限流

对整体限流,比如设置QPS到达100,这里限流会限制整个系统不可以.这就是粗度的限流。不过我们还是推荐使用之前细粒度的方法。

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

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

这种的系统规则主要做总控,使用注意下,也不是太多。

@SentinelResource注解:

按资源名称限流+后续处理:

  1. 启动Nacos和Sentinel成功。

  2. 修改cloudalibaba-sentinel-service8401

  3. POM:

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.atguigu.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
    </dependency>
  4. YML:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    server:
    port: 8401

    spring:
    application:
    name: cloudalibaba-sentinel-service
    cloud:
    nacos:
    discovery:
    server-addr: localhost:8848
    sentinel:
    transport:
    dashboard: localhost:8080
    port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口

    management:
    endpoints:
    web:
    exposure:
    include: '*'
  5. Controller:

    资源名就是主机里面Value的值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @RestController
    public class RateLimitController
    {
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource()
    {
    return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
    }
    public CommonResult handleException(BlockException exception)
    {
    return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
    }
  6. 配置资源限流规则:

    表示1秒钟内查询次数大于1,就跑到我们自定义的处理限流。

  7. 测试:

    • 1秒钟点击1下,ok。
    • 超多上述问题,疯狂点击,返回自己定义的限流处理信息,限流发送。

  8. 额外问题:

    • 此时关闭微服务8401查看
    • Sentinel控制台,流控规则消失了?

按照url地址限流+后续处理:

通过访问的URL来限流,会返回sentinel自带默认的限流处理信息。

  1. 代码:

    1
    2
    3
    4
    5
    6
    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl()
    {
    return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
    }

    这里没有兜底方法就会使用系统默认的。

  2. 访问:

上面兜底方案面临的问题:

客户自定义限流处理逻辑:

  1. 创建CustomerBlockHandler类用于自定义限流处理逻辑

  2. 自定义限流处理类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.aaron.alibaba.springcloud.myhandler;

    import com.aaron.springcloud.entities.CommonResult;
    import com.alibaba.csp.sentinel.slots.block.BlockException;


    public class CustomerBlockHandler
    {
    public static CommonResult handlerException(BlockException exception)
    {
    return new CommonResult(4444,"按客戶自定义,global handlerException----1");
    }
    public static CommonResult handlerException2(BlockException exception)
    {
    return new CommonResult(4444,"按客戶自定义,global handlerException----2");
    }
    }
  3. RateLimitController:

    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
    blockHandlerClass = CustomerBlockHandler.class,
    blockHandler = "handlerException2")
    public CommonResult customerBlockHandler()
    {
    return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
    }
  4. 启动微服务先调用一次

  5. Sentinel控制台配置。配置下限流规则。

  6. 测试后我们自定义的出来了

  7. 进一步说明

更多注解属性说明:

官网有更详细的解释。

服务熔断:

sentinel整合ribbon+openFeign+fallback。

记得启动nacos和sentinel。

Sentinel服务熔断整合Ribbon:

  1. 启动nacos和sentinel

  2. 提供者9003和9004

    1. 新建两个模块。

      1. cloudalibaba-provider-payment9003
      2. cloudalibaba-provider-payment9004
    2. POM

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      <dependencies>
      <!--SpringCloud ailibaba nacos -->
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      </dependency>
      <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
      <groupId>com.atguigu.springcloud</groupId>
      <artifactId>cloud-api-commons</artifactId>
      <version>${project.version}</version>
      </dependency>
      <!-- SpringBoot整合Web组件 -->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <!--日常通用jar包配置-->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
      </dependency>
      <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
      </dependencies>
    3. YML

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      server:
      port: 9003

      spring:
      application:
      name: nacos-payment-provider
      cloud:
      nacos:
      discovery:
      server-addr: localhost:8848 #配置Nacos地址

      management:
      endpoints:
      web:
      exposure:
      include: '*'
    4. 主启动:

      1
      2
      @SpringBootApplication
      @EnableDiscoveryClient
    5. 业务类:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      @RestController
      public class PaymentController
      {
      @Value("${server.port}")
      private String serverPort;

      public static HashMap<Long, Payment> hashMap = new HashMap<>();
      static{
      hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
      hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
      hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
      }

      @GetMapping(value = "/paymentSQL/{id}")
      public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
      Payment payment = hashMap.get(id);
      CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort: "+serverPort,payment);
      return result;
      }
      }
    6. 测试地址:localhost:9003/paymentSQL/1

  3. 消费者84

    1. cloudalibaba-consumer-nacos-order84

    2. POM:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      </dependency>
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
      </dependency>
      <dependency>
      <groupId>com.atguigu.springcloud</groupId>
      <artifactId>cloud-api-commons</artifactId>
      <version>${project.version}</version>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
      </dependency>
      <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
      </dependencies>
    3. YML:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      server:
      port: 84


      spring:
      application:
      name: nacos-order-consumer
      cloud:
      nacos:
      discovery:
      server-addr: localhost:8848
      sentinel:
      transport:
      dashboard: localhost:8080
      port: 8719

      service-url:
      nacos-user-service: http://nacos-payment-provider
    4. 主启动:

      1
      2
      3
      @EnableDiscoveryClient
      @SpringBootApplication
      @EnableFeignClients
    5. 业务类:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @Configuration
      public class ApplicationContextConfig
      {
      @Bean
      @LoadBalanced
      public RestTemplate getRestTemplate()
      {
      return new RestTemplate();
      }
      }

      还有一个CircleBreakerController:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      @RestController
      @Slf4j
      public class CircleBreakerController {

      public static final String SERVICE_URL = "http://nacos-payment-provider";

      @Resource
      private RestTemplate restTemplate;


      @RequestMapping("/consumer/fallback/{id}")
      //@SentinelResource(value = "fallback") //没有配置
      //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
      //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
      @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
      exceptionsToIgnore = {IllegalArgumentException.class})
      public CommonResult<Payment> fallback(@PathVariable Long id) {
      CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);

      if (id == 4) {
      throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
      }else if (result.getData() == null) {
      throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
      }

      return result;
      }

      //fallback
      public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
      Payment payment = new Payment(id,"null");
      return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
      }

      //blockHandler
      public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {
      Payment payment = new Payment(id,"null");
      return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
      }
      }

      注意:修改后请重启微服务,热部署对java代码生效及时,但对于@SentinelResource注解内属性,有时效果不好。

    6. 目的:

      1. fallback管运行异常

      2. blockHandler管配置违规,在Sentinel界面去配置的。

Sentinel服务熔断无配置:

测试地址:localhost:84/consumer/fallback/1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
@Slf4j
public class CircleBreakerController
{
public static final String SERVICE_URL = "http://nacos-payment-provider";

@Resource
private RestTemplate restTemplate;

@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")

public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);

if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}

return result;
}

既没有熔断也没有降级:

Sentinel服务熔断只配置fallback:

Sentinel服务熔断只配置blockHandler:

blockHandler只负责sentinel控制台的配置违规。

那么运行异常,就不会管了。

但是我快速点击,启动sentinel的配置作用,那么就会报:

Sentinel服务熔断配置fallback+blockHandler:

运行报错会出现兜底方法。

流控也会出现。

流控的作用大于兜底方法,也就是blockHandler>fallback

Sentinel服务熔断exceptionsToIgnore:

Sentinel服务熔断OpenFeign:

  1. 修改84模块

  2. POM

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  3. YML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    server:
    port: 84


    spring:
    application:
    name: nacos-order-consumer
    cloud:
    nacos:
    discovery:
    server-addr: localhost:8848
    sentinel:
    transport:
    dashboard: localhost:8080
    port: 8719

    service-url:
    nacos-user-service: http://nacos-payment-provider

    #对Feign的支持
    feign:
    sentinel:
    enabled: true
  4. 业务类

    带@FeignClient注解的业务接口

    1
    2
    3
    4
    5
    6
    @FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
    public interface PaymentService
    {
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
    }

    PaymentFallbackService实现类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    @Component
    public class PaymentFallbackService implements PaymentService
    {
    @Override
    public CommonResult<Payment> paymentSQL(Long id)
    {
    return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
    }
    }

    Controller:

    1
    2
    3
    4
    5
    6
    7
    8
    // OpenFeign
    @Resource
    private PaymentService paymentService;

    @GetMapping(value = "/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
    return paymentService.paymentSQL(id);
    }
  5. 主启动

    1
    2
    3
    @EnableDiscoveryClient
    @SpringBootApplication
    @EnableFeignClients
  6. 测试localhost:84/consumer/openfeign/1

    测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死。

熔断框架的比较:

Sentinel持久化规则:

默认规则是临时存储的,重启sentinel就会消失。

这里以之前的8401为案例进行修改:

  1. 修改8401的POM:

    1
    2
    3
    4
    5
    <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
    <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
  2. 修改配置文件:添加:

    实际上就是指定,我们的规则要保证在哪个名称空间的哪个分组下

    这里没有指定namespace, 但是是可以指定的。

    注意,这里的dataid要与8401的服务名一致

  3. 在nacos中创建一个配置文件,dataId就是上面配置文件中指定的:

  4. 启动8401:

  5. 关闭8401:

  6. 此时重启8401,如果sentinel又可以正常取到规则,那么证明持久化成功。可以看到,又重新出现了。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~

支付宝
微信