SpringCloud Alibaba Sentinel实现熔断与限流(下)

  • Post author:
  • Post category:其他



在上一篇讲了sentinel的介绍,安装Sentinel控制台、初始化演示工以及程流控规则



接下来学习熔断规则,热点key限流,系统规则,@SentinelResource注解,服务熔断功能以及规则持久化。



熔断规则





官方介绍


  • 基本介绍

    在这里插入图片描述


    熔断策略

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 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

    注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(

    BlockException

    )不生效。
  • RT(平均响应时间,秒级)

    平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级

    窗口期过后关闭断路器

    RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)


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

Field 说明 默认值
resource 资源名,即规则的作用对象
grade 熔断策略,支持慢调用比例/异常比例/异常数策略 慢调用比例
count 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow 熔断时长,单位为 s
minRequestAmount 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) 5
statIntervalMs 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) 1000 ms
slowRatioThreshold 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

Sentinel的断路器是

没有半开状态



半开的状态系统自动去检测是否请求有异常,

没有异常就关闭断路器恢复使用,

有异常则继续打开断路器不可用。具体可以参考Hystrix



(1)慢调用比例

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

  • 添加一个测试接口,调用接口时休眠2秒,使得此接口的响应时间为1秒以上
@GetMapping("/testD")
public String testD()
{
    //暂停几秒钟线程
    try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
    log.info("testD 测试调用比例");
    return "------testD";
}
  • 控制台添加测试接口熔断规则

    在这里插入图片描述

  • 最大 RT

    :慢调用临界 RT,超出该值计为慢调用。单位毫秒

  • 比例阈值

    :RT模式下慢速请求比率的阈值。默认1.0d

  • 熔断时长

    :断路器打开时的恢复超时(以秒为单位)。超时后,断路器将转换为半开状态以尝试一些请求。单位秒,图中表示触发熔断后,接下来的10秒内请求会自动被熔断,经过10S后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

  • 最小请求数

    :可以触发熔断中断的最小请求数(在有效的统计时间范围内)。默认值为5

  • StatIntervalMs

    : 统计时长(单位为 ms),如 601000 代表分钟级(1.8.0 引入),默认1000,在控制台没有选项,需代码实现。

使用

jemeter

测试

在这里插入图片描述
在这里插入图片描述

使用压测工具访问测试接口,查看限流效果图,具体限流过程大概为:

(1)请求进入后台,sentinel会根据设置的统计时长(默认1S)统计时间段内请求的总数

(2)首先判断统计的请求总数是否小于用户的设置的最小请求数(默认5),小于则不熔断,反之则进入下一步

(3)然后根据用户设置的最大 RT,判断统计中的请求是否为慢调用,大于设置值为是慢调用请求

(4)再次计算慢调用请求/总统计请求比例,是否超过设置的比例阈值。

(5)当统计时间内的请求数及慢调用比例阈值都超过设置的阈值后,接下来的熔断时长内请求会自动被熔断

(6)熔断时长结束后,熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

————————————————

版权声明:本文为CSDN博主「云烟成雨csdn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_43437874/article/details/111475762

结论:


永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,

如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了

后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK



(2)异常比例


异常比例 (ERROR_RATIO)

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

  • 在controller添加以下方法
@GetMapping("/testE")
public String testE()
{
    log.info("testE 测试异常比例");
    int age = 10/0;
    return "------testE";
}
  • 控制台添加测试接口熔断规则

    在这里插入图片描述

    访问

    http://127.0.0.1:8401/testE


    在这里插入图片描述

    直接报错了,直接再刷新。

    在这里插入图片描述

    因为多次调用

    达到我们的配置熔断降级条件了,断路器开启(保险丝跳闸)

    ,微服务不可用了,不再报错error而是服务降级了。



(3)异常数

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


测试


还是刚刚那个

testE

路径

  • 控制台修改测试接口熔断规则

    在这里插入图片描述

访问

http://127.0.0.1:8401/testE


在这里插入图片描述

5s内,先访问5次,当访问了第五次了,在访问一次,也就是第六次。

在这里插入图片描述

因为我们配置的统计时长是

5s

,当5s内异常的次数达到了

5次

,第

6次

就进行熔断降级,降级持续

2s

钟。



热点key限流


何为热点?



热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作




官方说明

  • 在controller添加以下方法
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1, 
                         @RequestParam(value = "p2",required = false) String p2){
    return "------testHotKey";
}
public String dealHandler_testHotKey(String p1,String p2,BlockException exception)
{
    return "-----dealHandler_testHotKey";
}


sentinel系统默认的提示:Blocked by Sentinel (flow limiting)

  • 配置说明

    在这里插入图片描述

    正常访问

    http://127.0.0.1:8401/testHotKey


    在这里插入图片描述

    访问

    http://127.0.0.1:8401/testHotKey?p1=1

    ,并不断刷新

    在这里插入图片描述

    访问

    http://127.0.0.1:8401/testHotKey?p2=1

    ,并不断刷新

    在这里插入图片描述

    访问

    http://127.0.0.1:8401/testHotKey?p2=1&p1=1

    ,并不断刷新

    在这里插入图片描述


    结论,只要带有

    p1

    这个参数并且QPS超过每秒1次都要被降级处理。
@SentinelResource(value = "testHotKey")//异常打到了前台用户界面看到,不友好
//方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")//用了我们自己定义的


上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流,但是我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样,例如:假如当p1的值等于5时,它的阈值可以达到200

  • 修改配置

    在这里插入图片描述

    配置说明:当p1等于5的时候,阈值变为200,当p1不等于5的时候,阈值就是平常的1。




注意:热点参数的注意点,参数必须是基本类型或者String


其他:



@SentinelResource



处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;


RuntimeException



int age = 10/0,这个是java运行时报出的运行时异常RunTimeException,@SentinelResource不管


总结



@SentinelResource主管配置出错,运行出错该走异常走异常




系统规则

系统保护规则是从应用级别的

入口流量

进行控制,从单台机器的

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.按资源名称限流以及后续处理

  • 添加新的

    RateLimitController
@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 服务不可用");
    }
}
  • 启动nacos,启动sentinel,去sentinel控制台添加

    流控规则


    在这里插入图片描述

    配置说明:


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


    在这里插入图片描述


    1秒钟点击1下,没法发生降级。


    在这里插入图片描述


    超过上述,疯狂点击,返回了自己定义的限流处理信息,限流发生


    在这里插入图片描述



2.按照Url地址限流以及后续处理

在刚刚新建的类

RateLimitController

添加以下方法

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

Sentinel控制台配置,新增以下配置

在这里插入图片描述

访问

http://localhost:8401/rateLimit/byUrl


很慢的访问时

在这里插入图片描述

疯狂点击

http://localhost:8401/rateLimit/byUrl


在这里插入图片描述

成功的进行了限流



3.客户自定义限流处理逻辑

上面兜底代码存在的问题:

  • 系统默认的,没有体现我们自己的业务要求。
  • 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
  • 每个业务方法都添加一个兜底的,那代码膨胀加剧。
  • 全局统一的处理方法没有体现。


创建

CustomerBlockHandler

类用于自定义限流处理逻辑

public class CustomerBlockHandler
{
    public static CommonResult handleException(BlockException exception){
        return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler");
    }
}

在这里插入图片描述



RateLimitController

添加新的方法

     /**
     * 自定义通用的限流处理逻辑
     blockHandlerClass = CustomerBlockHandler.class
     blockHandler = handleException
     上述配置:找CustomerBlockHandler类里的handleException方法进行兜底处理
     */
    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException")
    public CommonResult customerBlockHandler()
    {
        return new CommonResult(200,"按客户自定义限流处理逻辑");
    }

配置进一步说明

在这里插入图片描述

启动微服务后先访问一次

http://localhost:8401/rateLimit/customerBlockHandler


Sentinel控制台配置

在这里插入图片描述

启动微服务后先疯狂访问

http://localhost:8401/rateLimit/customerBlockHandler


在这里插入图片描述



更多注解属性说明

关于

@SentinelResource

注解最主要的两个用法:限流控制和熔断降级的具体使用案例介绍完了。另外,该注解还有一些其他更精细化的配置,比如忽略某些异常的配置、默认降级函数等等,具体可见如下说明:


  • value

    :资源名称,必需项(不能为空)

  • entryType

    :entry 类型,可选项(默认为 EntryType.OUT)

  • blockHandler / blockHandlerClass

    :

    blockHandle

    r对应处理

    BlockException

    的函数名称,可选项。

    blockHandler

    函数访问范围需要是

    public

    ,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为

    BlockException



    blockHandler

    函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定

    blockHandlerClass

    为对应的类的

    Class

    对象,注意对应的函数必需为

    static

    函数,否则无法解析。

  • fallback



    fallback

    函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。

    fallback

    函数可以针对所有类型的异常(除了

    exceptionsToIgnore

    里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:

    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个

      Throwable

      类型的参数用于接收对应的异常。

    • fallback

      函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定

      fallbackClass

      为对应的类的

      Class

      对象,注意对应的函数必需为 static 函数,否则无法解析。

  • defaultFallback(since 1.6.0)

    :默认的

    fallback

    函数名称,可选项,通常用于通用的

    fallback

    逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了

    exceptionsToIgnore

    里面排除掉的异常类型)进行处理。若同时配置了

    fallback



    defaultFallback

    ,则只有 fallback 会生效。defaultFallback 函数签名要求:

    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个

      Throwable

      类型的参数用于接收对应的异常。

    • defaultFallback

      函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

  • exceptionsToIgnore

    (since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException

直接抛出



服务熔断功能


  • sentinel整合ribbon+openFeign+fallback

  • 启动nacos和sentinel



创建服务提供者cloudalibaba-provider-payment9003/9004


  • 新建cloudalibaba-provider-payment9003


    在这里插入图片描述


  • 修改pom文件,添加以下依赖

 <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.zhubayi.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>

  • 添加

    application.yml

    配置文件
 
server:
  port: 9003

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

management:
  endpoints:
    web:
      exposure:
        include: '*'

  • 主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
    public static void main(String[] args) {
            SpringApplication.run(PaymentMain9003.class, args);
    }
}

  • controller
 
package com.zhubayi.springcloud.alibaba.controller;

import com.zhubayi.springcloud.entities.CommonResult;
import com.zhubayi.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

@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;
    }
}

在这里插入图片描述


  • 9004模块也是,要记得改端口号和启动类

在这里插入图片描述

在这里插入图片描述

启动9003和9004以及nacos

测试

http://127.0.0.1:9003/paymentSQL/1


在这里插入图片描述

测试

http://127.0.0.1:9004/paymentSQL/1


在这里插入图片描述



新建

cloudalibaba-consumer-nacos-order84

服务消费者模块


  • 修改pom文件,添加以下的依赖
<dependencies>
        <!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.zhubayi.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>

  • 新建

    application.yml

    配置文件
server:
  port: 8400


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

#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

  • 启动类

    OrderNacosMain84

@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84
{
    public static void main(String[] args) {
            SpringApplication.run(OrderNacosMain84.class, args);
    }
}



Ribbon系列


  • 新建配置类

    ApplicationContextConfig

    ,向容器注入Bean
@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

  • controller
package com.zhubayi.springcloud.alibaba.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zhubayi.springcloud.entities.CommonResult;
import com.zhubayi.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.concurrent.Executors;

@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;
    }
}

启动84端口,测试

http://192.168.0.159:8400/consumer/fallback/1


在这里插入图片描述

在刷新一下

在这里插入图片描述




没有任何配置

当访问

http://192.168.0.159:8400/consumer/fallback/4


在这里插入图片描述


给客户error页面,不友好




只配置fallback


修改controller,添加兜底方法

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

完整

CircleBreakerController

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

测试

在这里插入图片描述

成功执行了兜底方法




只配置blockHandler


修改controller代码

@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",fallback = "handlerFallback") //fallback只负责业务异常 handlerFallback兜底方法
     @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler负责在sentinel里面配置的降级限流
    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 ("非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录");
        }
        return result;
    }
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"fallback,无此流水,exception  "+e.getMessage(),payment);
    }
    public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }
}

添加流控规则

在这里插入图片描述

疯狂访问

http://192.168.0.159:8400/consumer/fallback/1

在这里插入图片描述




fallback和blockHandler都配置

  • 修改

    @SentinelResource

    注解
 @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")


若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。fallback只负责业务异常 handlerFallback兜底方法,blockHandler负责在sentinel里面配置的降级限流


忽略属性

 @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class}) //exceptionsToIgnore忽略这个异常,就是这个异常不处理

在这里插入图片描述




Feign系列

  • pom文件记得添加openfeign的依赖
<!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
  • 修改配置文件激活Sentinel对Feign的支持
# 激活Sentinel对Feign的支持
feign:
  sentinel:
    enabled: true  
  • 业务类

    IPaymentService
/**
 * 使用 fallback 方式是无法获取异常信息的,
 * 如果想要获取异常信息,可以使用 fallbackFactory参数
 */
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)//调用中关闭9003服务提供者
public interface IPaymentService
{
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
 


PaymentFallbackService

@Component
public class PaymentFallbackService implements IPaymentService{
    /**
     * 远程调用
     *
     * @param id
     * @return
     */
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(444,"服务降级返回",new Payment(id,"error"));
    }
}


controller

//==================OpenFeign
//解决办法https://blog.csdn.net/kongliand/article/details/108058831
    @Resource
    private IPaymentService ipaymentService;
    @GetMapping(value = "/consumer/openfeign/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
        if(id == 4)
        {
            throw new RuntimeException("没有该id");
        }
        return ipaymentService.paymentSQL(id);
    }
  • 在主启动类上添加

    EnableFeignClients

    注解,开启远程调用

测试:把9003、9004都关了,然后访问

http://192.168.0.159:8400/consumer/openfeign/1


在这里插入图片描述

成功进行了降级。




规则持久化


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

  • 将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台

    的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效

  • 修改

    cloudalibaba-sentinel-service8401


  • 添加依赖
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

  • 修改配置文件
 
spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow


完整配置文件

 
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
      #sentinel持久化
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

management:
  endpoints:
    web:
      exposure:
        include: '*'

feign:
  sentinel:
    enabled: true # 激活Sentinel对Feign的支持


  • 添加Nacos业务规则配置


    在这里插入图片描述

    配置dataId、配置组和配置格式的由来

在这里插入图片描述

配置说明:

resource:资源名称;

limitApp:来源应用;

grade:阈值类型,0表示线程数,1表示QPS;

count:单机阈值;

strategy:流控模式,0表示直接,1表示关联,2表示链路;

controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;

clusterMode:是否集群。

启动8401后

刷新sentinel

发现业务规则有了

在这里插入图片描述


测试


快速访问测试接口

http://192.168.0.159:8401/rateLimit/byUrl


在这里插入图片描述


停止8401再看sentinel


在这里插入图片描述


发现流控规则没有了,重新启动8401再看sentinel



刚开始看还是没有,去访问

http://192.168.0.159:8401/rateLimit/byUrl

或者

http://127.0.0.1:8401/rateLimit/byUrl

,重新配置出现了,持久化验证通过



版权声明:本文为qq_44732146原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。