SpringCloud Alibaba Sentinel熔断与限流

SpringCloud Alibaba Sentinel熔断与限流

在这里插入图片描述
作者:pinkhub
撰写时间:2023/4/2


1.1 概述

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

Hystrix的缺点:

1)需要手工搭建监控平台

2)没有一套web界面进行细粒度的配置流量控制、速率控制、服务熔断、服务降级…

Sentinel 解决的问题:服务雪崩、服务降级、服务中断、服务限流

Sentinel 分为两个部分:

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

Sentinel 的主要特性:
在这里插入图片描述

Sentinel 的开源生态:

在这里插入图片描述

学习资料

官网:https://github.com/alibaba/Sentinel

中文官网:https://github.com/alibaba/Sentinel/wiki

Sentinel 社区官方网站:https://sentinelguard.io/

spring_cloud_alibaba_sentinel:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

安装Sentinel控制台

下载地址:https://github.com/alibaba/Sentinel/releases

在这里插入图片描述

java -jar sentinel-dashboard-1.7.0.jar

在这里插入图片描述

NOTE:登录账号、密码:sentinel

在这里插入图片描述

1.2 初始化工程

需求:通过sentinel监控微服务8401

【第一步】启动Nacos8848

nacos安装目录下的bin目录,双击startup.cmd

【第二步】创建微服务8401

cloudalibaba-sentinel-service8401

1)修改pom文件添加依赖,

<dependencies>
    <!--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>
    <!-- SpringBoot整合Web组件+actuator -->
    <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>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>4.6.3</version>
    </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>

2)创建application.yml配置文件,

server:
  port: 8401

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

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

3)创建启动类,

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

4)创建业务类,controller

@RestController
public class FlowLimitController {
    
    
    @GetMapping("/testA")
    public String testA()
    {
    
    
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB()
    {
    
    
        return "------testB";
    }
}

【第三步】启动Sentinel8080

java -jar sentinel-dashboard-1.7.0.jar

【第四步】启动微服务8401

【第五步】查看sentinel控制台 http://localhost:8080/

现象:控制台什么都没有

解释:Sentinel采用的懒加载机制,执行一次访问即可

测试地址1:http://localhost:8401/testA

测试地址2:http://localhost:8401/testB

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2l3ku34v-1680441419227)(assets/image-20230402110434404.png)]

1.3 流控规则

一、基本介绍

在这里插入图片描述

字段说明

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

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

3)阈值类型 / 单机阈值

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

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

4)是否集群:不需要集群

5)流控模式

  • 直接

  • 关联

  • 链路

6)流控效果

  • 快速失败

  • Warm Up

  • 排队等待

二、流控模式

直接

请求达到限流条件时,直接限流→快速失败 系统默认

在这里插入图片描述

快速点击访问,http://localhost:8401/testA

结果:Blocked by Sentinel (flow limiting)

思考:默认fallback直接调用默认报错信息,自定义 fallback兜底方法

关联

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

解释:当与A关联的资源B达到阀值后,就限流A自己

在这里插入图片描述

测试:/testA请求关联/testB请求,/testB请求达到阈值时,限流/testA请求

说明: JMeter做压力测试,3秒100个请求循环100次

在这里插入图片描述

在这里插入图片描述

请求:http://localhost:8401/testA

测试效果:Blocked by Sentinel (flow limiting)

链路

只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【API 级别的针对来源】

场景:多个请求调用了同一个微服务

三、流控效果

快速失败

直接失败,抛异常 Blocked by Sentinel (flow limiting)

源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

NOTE:直接->快速失败(默认的流控处理)

Warm UP

预热/冷启动方式,根据 codeFactory(冷加载因子,默认 3),从阈值 /CodeFactor,经过预热时长,才达到设置的 QPS 阈值 。

NOTE:Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

官网:https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8

案例,阀值为10,预热时长设置5秒。

系统初始化的阀值为10 / 3 约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10

在这里插入图片描述

多次点击http://localhost:8401/testA,查看效果

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

排队等待

匀速排队,让请求以匀速的速度通过,阈值类型必须设置 QPS,否则无效

设置含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

在这里插入图片描述

源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

1.4 熔断降级

问题引入

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

Sentinel 熔断降级

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

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

Sentinel断路器是没有半开状态,半开的状态,系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用,具体可以参考 hystrix。

官网:https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

降级策略

在这里插入图片描述

1)RT(平均响应时间,秒级)

平均响应时间   超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级  
             窗口期过后关闭断路器
             RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

2)异常比例(秒级)

QPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

3)异常数(分钟级)

异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

降级策略实战

1)平均响应时间RT

平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入5个请求,平均响应时间(秒级)超过阈值(count,以 ms 为单位),那么在接下的时间窗口 (DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。

Sentinel 默认统计的RT上限是4900 ms, 超出此阈值的都会算作 4900ms,若需要修改此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx 来配置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xpxGKmQp-1680441419233)(assets/image-20230402140806627.png)]

添加代码,

@GetMapping("/testD")
public String testD(){
    
    
    //暂停几秒钟线程
    try {
    
     TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
    
     e.printStackTrace(); }
    log.info("testD 测试RT");
    return "------testD";
}

Jmeter压力测试设置,1秒10个请求,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Um0lqHzn-1680441419233)(assets/image-20230402140716401.png)]

测试地址:http://localhost:8401/testD,发现服务降级

说明:一秒钟进来10个线程(大于5个)调用testD,希望200毫秒处理完本次任务,如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了,停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复正常。

在这里插入图片描述

2)异常比例

异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= 5(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之 后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow, 以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

添加业务代码,

@GetMapping("/testC")
public String testC()
{
    
    
    log.info("testC 测试异常比例");
    int age=10/0;
    return "------testC";
}

配置降级规则和JMeter压测,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cvFQ9fnL-1680441419234)(assets/image-20230402142339427.png)]

按照上述配置,访问接口:http://localhost:8401/testC

单独访问一次,必然来一次报错一次(int age = 10/0),调一次错一次;

开启jmeter后,直接高并发发送请求,多次调用达到我们的配置条件了。 Blocked by Sentinel (flow limiting)

断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务降级了。

3)异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近1分钟的异常数目超过阈值之后会进行熔断。

NOTE:时间窗口一定要大于等于 60 秒 ;异常数按分钟来统计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CZIuvYyf-1680441419234)(assets/image-20230402143941573.png)]

添加业务代码,

@GetMapping("/testE")
public String testE()
{
    
    
    log.info("testE 测试异常数");
    int age=10/0;
    return "------testE";
}

http://localhost:8401/testE,1-5次访问报错,因为除数不能为零,达到5次报错后,进入熔断后降级。

Blocked by Sentinel (flow limiting)

1.5 热点key限流

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

兜底方法fallback分为两种:系统默认和客户自定义,sentinel系统默认的提示:Blocked by Sentinel (flow limiting),类似hystrix,某个方法出问题了,就找对应的兜底降级方法? 从@HystrixCommand@SentinelResource

官网:https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

源码:com.alibaba.csp.sentinel.slots.block.BlockException

在这里插入图片描述

热点限流实战

需求:testHotKey方法第一个参数只要QPS超过每秒1次,马上降级处理,执行自定义兜底方法deal_testHotKey

【第一步】添加业务代码

@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){
    
    
    return "----------testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception){
    
    
    return "____________deal_testHotKey____________";
}

NOTE:

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

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

【第二步】配置热点规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPRdxxv4-1680441419234)(assets/image-20230402150043007.png)]

说明:

1)资源名为路径名或者@SentinelResource注解的value值

2)限流模式只支持QPS模式,单机阀值以及统计窗口时长表示在此窗口时间超过阀值就限流。

3)@SentinelResource注解的方法,参数索引,0代表第一个参数,1代表第二个参数,以此类推

【第三步】测试

测试地址:

http://localhost:8401/testHotKey?p1=abc 服务降级

http://localhost:8401/testHotKey?p1=abc&p2=ddd 服务降级

http://localhost:8401/testHotKey?p2=abc

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GLV9GeJU-1680441419235)(assets/image-20230402150326318.png)]

参数例外项

需求:当p1的值等于5时,它的阈值可以达到200;当p1不等于5的时候,阈值就是1。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1L2kLQrq-1680441419235)(assets/image-20230402152440649.png)]

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

测试:

http://localhost:8401/testHotKey?p1=5 阈值可至200

http://localhost:8401/testHotKey?p1=3 阈值为1,直接限流

1.6 系统自适应限流

Sentinel系统自适应过载保护,从整体维度对应用入口流量进行控制,结合应用的 Load负载、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

官网: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

系统规则

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCoph3ar-1680441419235)(assets/image-20230402154118950.png)]

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

1)Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5

2)CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。

3)平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

4)并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。

5)入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

1.7 @SentinelResource

一、资源名称限流+fallback

添加业务代码

@RestController
public class RateLimitController {
    
    
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource()
    {
    
    
        return new CommonResult(200,"按资源名称限流测试OK",new Payment(2023L,"serial001"));
    }
    public CommonResult handleException(BlockException exception)
    {
    
    
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xm30g7ys-1680441419235)(assets/image-20230402160118341.png)]

NOTE:关闭8401服务,Sentinel控制台流控规消失,临时性。

二、Url地址限流+fallback

修改业务代码

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tQXmD20K-1680441419235)(assets/image-20230402160827967.png)]

http://localhost:8401/rateLimit/byUrl,触发默认兜底方法,Blocked by Sentinel (flow limiting)

三、上面兜底方案面临的问题

1)系统默认的,没有体现我们自己的业务要求。

2 )依照现有条件,自定义的处理方法又和业务代码耦合在一块,不直观。

3 )每个业务方法都添加一个兜底的,代码膨胀加剧。

4 )全局统一的处理方法没有体现。

四、自定义限流处理逻辑

需求:自定义全局统一的处理方法——fallback兜底方法

参考文档:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

【一】创建自定义限流处理类

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

NOTE:注解方式埋点不支持 private 方法。

【二】修改业务类

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(
    value = "customerBlockHandler",
    blockHandlerClass= CustomerBlockHandler.class,
    blockHandler ="handleException2")
public CommonResult customerBlockHandler(){
    
    
    return new CommonResult(200,"按客户自定义限流处理逻辑",new Payment(2023L,"serial003"));
}

【三】修改流控规则,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qc6ZcRXZ-1680441419235)(assets/image-20230402163143707.png)]

【四】测试地址:http://localhost:8401/rateLimit/customerBlockHandler

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ECGMQG08-1680441419236)(assets/image-20230402163245970.png)]

1.8 服务熔断

需求:sentinel整合ribbon+openFeign+fallback

一、Ribbon系列

服务提供方9003|9004

新建服务提供者模块cloudalibaba-provider-payment9003cloudalibaba-provider-payment9004

cloudalibaba-provider-payment9003为例

【第一步】创建Module

cloudalibaba-provider-payment9003

【第二步】添加项目依赖

<dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.example.cloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</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);
    }
}

【第五步】编写业务类

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

}

【第六步】测试

测试地址1:http://localhost:9003/paymentSQL/1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FolFIa6J-1680441419236)(assets/image-20230402200911204.png)]

测试地址2:http://localhost:9004/paymentSQL/1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u5r6TeB4-1680441419236)(assets/image-20230402165812467.png)]

服务消费者84

【第一步】创建

新建服务消费者模块cloudalibaba-consumer-nacos-order84

【第二步】添加依赖

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

【第四步】创建启动类

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

【第五步】创建业务类

配置类

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

controller

@RestController
@Slf4j
public class CircleBreakerController {
    
    
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    PaymentFallbackService paymentFallbackService;

    @Resource
    private OrderService orderService;

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

总结:fallback负责业务异常;blockHandler负责在sentinel里面配置的降级限流

【第五步】测试

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22RNb6lB-1680441419236)(assets/image-20230402170525738.png)]

测试情况:

1)没有任何配置

2)只配置fallback

3)只配置blockHandler

4)fallback和blockHandler都配置

二、Feign系列

需求:修改cloudalibaba-consumer-nacos-order84模块,84消费者调用提供者9003。

Feign组件一般是消费方

【第一步】添加依赖

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

【第二步】修改application.yml

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

【第三步】启动类添加@EnableFeignClients

【第四步】修改业务类

定义注解+接口

@Component
@FeignClient(value="nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface OrderService {
    
    
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

自定义fallback兜底方法

@Component
public class PaymentFallbackService implements OrderService {
    
    
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
    
    
        return new CommonResult<>(444,"服务降级返回,没有该流水信息",new Payment(id, "errorSerial......"));
    }
}

controller

@RestController
@Slf4j
public class CircleBreakerController {
    
    
    @Autowired
    private OrderService orderService;

    @GetMapping(value = "/consumer/openfeign/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
    
    
        if(id == 4)
        {
    
    
            throw new RuntimeException("没有该id");
        }
        return orderService.paymentSQL(id);
    }
}

【第五步】测试

接口地址:http://localhost:84/consumer/openfeign/1

测试84调用9003,关闭9003微服务提供者,84消费方自动降级,不会一直等待

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xwn4VHox-1680441419236)(assets/image-20230402204829720.png)]

1.9 规则持久化

问题引入:一旦重启应用,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>

【第二步】修改application.yml配置

spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

【第三步】添加Nacos业务规则配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3DERilez-1680441419237)(assets/image-20230402205548948.png)]

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

说明:

1)resource:资源名称;

2)limitApp:来源应用;

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

4)count:单机阈值;

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

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

7)clusterMode:是否集群。

【第四步】测试

启动8401后,刷新sentinel,发现业务规则有了,访问http://localhost:8401/rateLimit/byUrl,默认fallback,Blocked by Sentinel (flow limiting)

停止8401再看sentinel,流控规则消失了

重新启动8401再看sentinel,多次调用请求接口http://localhost:8401/rateLimit/byUrl,流控规则出现,持久化测试通过。

猜你喜欢

转载自blog.csdn.net/weixin_44490884/article/details/129917421