一.概念

1.Sentinel是什么?

  • Sentinel: 分布式系统的流量防卫兵
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel

2.Sentinel特征?

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

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

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
    完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
    完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
    完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

3.Sentinel作用?

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_02

二.安装并运行Sentinel控制台

1.下载地址

https://github.com/alibaba/Sentinel/releases

2.运行命令

java -jar sentinel-dashboard-1.7.0.jar
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_03

3.访问

运行成功访问localhost:8080
用户名,密码为:sentinel
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_04

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_05

三.使用sentinel的基本配置

1.导入依赖

 <dependencies>

        <dependency>
            <groupId>com.sise</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!--nacos-discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <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>

        <!--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>

        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.3</version>
        </dependency>
        <!--lombok-->
        <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>

2.配置文件

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: '*'

3.启动类

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

4.controller业务类

@RestController
public class sentinelController {
    @GetMapping("/testA")
    public String testA(){
        return "this is TestA";
    }

    @GetMapping("/testB")
    public String testB(){
        return "this is TestB";
    }

}

5.测试

先启动nacos和sentinel
nacos已经注册上
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_06
打开sentinel面板,成功注册到sentinel

(注意:sentinel默认采用的是懒加载,如果该服务没有访问过,在sentinel不会显示出来,要先访问该服务的testA和testB)
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_07
访问几次testA,Sentinel流量监控到了

注意:但某个请求一段时间没被访问后sentinal的监控面板会清除,当有请求进来时会继续显示
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_08
sentinel8080正在监控微服务8401

四.流控规则

1.基本介绍

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_09

  • 资源名:唯一名称,默认请求路径

  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)

  • 阈值类型/单机阈值:

  1. QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流

  2. 线程数:当调用该api的线程数达到阈值的时候,进行限流

  • 是否集群:(不)需要集群

  • 流控模式:

  1. 直接:api达到限流条件时,直接限流

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

  3. 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)

  • 流控效果:

  1. 快速失败:直接失败,抛异常

  2. Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。

  3. 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS。否则无效。

2.直接快速失败

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_10

  • 测试:当每秒请求阈值大于1时报错
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_11

  • 阈值类型为线程数
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_12

  • 为了演示效果给TestA延迟处理时间

@GetMapping("/testA")
public String testA(){
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "this is TestA";
}

  • 打开一个窗口访问testA,立即再打开一个浏览器访问TestA表示两个线程去访问TestA
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_13

3.关联快速失败

  • 当关联的资源达到阈值时,就限流自己
    当与A关联的资源B达到阀值后,就限流A自己

  • 设置关联资源/testB:testb达到阀值后,就限流testa自己
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_14

  • 使用postman模拟访问/testB

20个进程,每0.3秒访问一次
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_15

  • 运行,并执行testA,查看是否会关联到,成功,当postman停止之后恢复正常
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_16

4.流控效果——Warm Up

  • 公式
    预热时长的时间内允许的QPS阈值:阈值除以coldFactor(默认值为3),
    超过预热时长后允许的QPS阈值:阈值

  • 解释
    Warm Up方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过“冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_17

  • Warmup配置
    默认coldFactor为3,即请求QPS从threshold / 3开始,经预热时长逐渐升至设定的QPS阈值。
    秒杀系统在开启的瞬间,会有很多流量进来,很有可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_18

  • 测试
    结果是前5s一直访问会访问不了,5s后可以正常访问
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_19

5.流控效果——排队等待

  • 解析

匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。
设置含义: /testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

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

  • 配置
    testB阈值最高为1,当QPS大于1时要排队等待,超时时间为20秒
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_20

  • 使用postman测试
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_21

  • 结果
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_22
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_23


五.降级规则

1.基本介绍

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_24
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_25
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_26

2.RT

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_27
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_28

  • 编辑降级规则
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_29

  • 使用jmeter工具进行测试
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_30

  • 测试结果
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_31
    Jmeter停止之后,正常访问
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_32

  • 结论
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_33

3.异常比例

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_34

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_35

  • 编辑降级规则

如果返回比例20% 以上都是异常,那么3秒内就触发降级
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_36

  • 测试
    由于异常比例的规则是,但每秒的请求>=5和异常比例>一定的阈值就才会触发,所以使用jmete工具测试,使用jmeter发送每秒10个请求,10个请求的异常比例是百分之百,所以满足条件触发降级
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_37

  • 结论
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_38

4.异常数

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_39
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_40
异常数是按照分钟统计的

  • 新增降级规则:61秒内,当异常数大于5开启降级

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_41

  • 测试
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_42
    前5次
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_43
    后五次
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_44


六.热点key限流

1.使用名称添加限规则

  • 新增RateLimitController

@RestController
public class RateLimitController {

//通过资源名称限流,限流是使用自定义的处理器(handleException)
    @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  服务不可用");
    }
}

  • 使用名称添加限规则

使用SentinelResource的value名添加限流规则,限流是使用自定义得处理器blockHandler,不使用Sentinel默认的提示
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_45

  • 测试

限流之后,使用的是自定义的处理信息,如果使用url的话,就会又变成Sentinel默认的提示信息(但是服务重启后,规则就消失了,所以这个是一个临时规则)
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_46

  • 重启后
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_47

2.使用url添加限规则

  • 新增业务类

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

  • 使用url添加限规则

使用url /rateLimit/byUrl 添加限规则后,只会使用Sentinel默认的提示信息
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_48

  • 测试
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_49

3.全局处理方法

  • 上面出现的问题
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_50

  • 编写全局处理类 CustomerBlockHandler

public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception){
        return new CommonResult(  444, "按客户自定义,global handlerException-------1 ");
    }
    public static CommonResult handlerException2(BlockException exception){
        return new CommonResult(  444, "按客户自定义,global handlerException-------2 ");
    }
}

  • controller新增方法

//通过名称customerBlockHandler限流之后
//使用的是自定义的处理信息CustomerBlockHandler类中的handlerException2方法
@GetMapping("/rateLimit/customerBlockHandler" )
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class,blockHandler="handlerException2")
    public CommonResult customerBlockHandler(){
        return new CommonResult(  200, "按客户自定义",new Payment( 2020L,  "seria1003"));
    }

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_51

  • 使用资源名customerBlockHandler,添加限流规则
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_52

  • 测试
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_53

4.热点参数限流

  • 添加controller方法

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

  • 编辑热点规则
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_54

  • 重启测试
    正常
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_55

    含有p1的参数且超过阈值
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_56
    只要不含有p1的参数,不管是否超过阈值,都为正常
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_57

  • 参数例外项
    我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样,假如当p1的值等于6时,它的阈值可以达到100

  • 编辑热点规则
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_58

  • 测试
    当p1为6时,他的阈值为100,不限流
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_59

  • 异常
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_60

七.服务熔断-Ribbon系列

1.启动nacos和sentinel

2.新建服务提供者9003/9004

  • 导入依赖

    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.sise</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>
        <!--lombok-->
        <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>

  • 配置文件

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

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

}

3.创建消费者84

  • 导入依赖

        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--nacos discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.sise</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--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>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!--lombok-->
        <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>

  • 配置文件

server:
  port: 84


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

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

  • 启动类

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

  • 负载均衡配置类

@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

  • service

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

  • 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") //没有配置
    //@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);
    }

    //==================OpenFeign
    @Resource
    private PaymentService paymentService;

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

4.fallback运行异常

  • 84一开始的业务类是这样的

    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback") //没有配置,程序出现异常会出现error界面
    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;
    }

  • 没配置降级或熔断之前,访问4以前的是正常,4之后的会出现错误页面,抛异常非法参数
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_61
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_62

  • 配置fallback后,新增一个handlerFallback处理方法,fallback负责业务异常(运行的异常),不负责控制台违规(限流或者熔断不负责)

@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback") //没有配置,程序出现异常会出现error界面
@SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback只负责业务异常
//@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("IllegalArgumentException,非法参数异常....");
    } else if (result.getData() == null) {
        throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
    }

    return result;
}

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

  • 测试
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_63

5.blockHandler配置违规

如果只配置blockHandler,测试java异常时会不会处理?

@RequestMapping("/consumer/fallback/{id}")
@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("IllegalArgumentException,非法参数异常....");
    } else if (result.getData() == null) {
        throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
    }

    return result;
}

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

  • 新增降级规则
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_64
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_65

  • 测试
    配置blockHandler后,新增一个blockHandler处理方法,blockHandler负责配置的控制台违规(限流或者熔断不负责),不负责业务异常(运行的异常)

点击请求一次,达不到降级规则,还是出现异常页面
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_66
一直点击请求,当达到降级规则时,才会出现定制页面
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_67

6.配置fallback和blockHandler

  • 修改注解

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

  • 添加流控规则,每秒请求只能有1个,多了限流
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_68
    测试

点击一秒一次,进入fallback
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_69
达到流控规则,进入blockHandler
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_70

  • 结论
    SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_71

7.exceptionsToIngnore属性

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_72

  • 测试

参数为5时,进入fallback方法
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_73

参数为4时
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_74


八.服务熔断-Feign系列

1.修改84模块,添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.添加配置文件

#对Feign的支持
feign:
  sentinel:
    enabled: true

3.添加feign接口

@FeignClient(value = "nacos-payment-provider")
public interface paymentService {
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

4.编写实现类

用于处理熔断后的处理方法,注意加@Component

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

5.指定对应的类,来处理降级

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_75

6.添加controller

// OpenFeign
@Resource
private PaymentService paymentService;

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

7.@EnableFeignClients

在主启动类上添加开启feign注解@EnableFeignClients
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_76

8.测试

测试84调用9003,9004,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_77


九.Sentinel持久化规则

一般我们在sentinel中制定一个规则给服务后,当该服务重启后规则就没了,所以需要配合nacos进行持久化

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效

以cloud-alibaba-sentinel-service8401为测试

1.添加依赖

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

2.添加配置

spring:
   cloud:
    sentinel:
    datasource:
     ds1:
      nacos:
        server-addr:localhost:8848
        dataid:${spring.application.name}
        groupid:DEFAULT_GROUP
        data-type:json
            rule-type:flow

支持多种持久化,这里采用nacos持久化
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_78

3.在nacos里新建配置

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_79
内容解释:

/rateLimit/byUrl对这个请求进行限流,可以配置多个请求

[
{
“resource”: “/rateLimit/byUrl”,
“limitApp”: “default”,
“grade”: 1,
“count”: 1,
“strategy”: 0,
“controlBehavior”: 0,
“clusterMode”: false
}
]
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_80
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_81

4.运行8401就直接有流控规则在

SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_82

5.测试

请求达到限流规则,进行限流处理
SpringCloud Alibaba—Sentinel熔断与限流_Sentinel_83

处理规则服务启动先去nacos拿限流配置文件,匹配规则前面nacos笔记有,然后注册sentinel时限流配置一起注册配置上去

 

 

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