03-13、SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel

SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel

一、Sentinel概述

1、Sentinel是什么

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

一句话概括:sentinel即Hystrix的替代品

官网: https://sentinelguard.io/zh-cn/

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

2、Sentinel基本概念

2.1、资源:

xml 复制代码
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调
用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,
URL,甚至服务名称作为资源名来标示资源。

2.2、规则:

xml 复制代码
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

2.3、流量控制:

xml 复制代码
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度
上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能
力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状.


流量控制有一下几个角度:
1、资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
2、运行指标,例如 QPS、线程池、系统负载等;
3、控制的效果,例如直接限流、冷启动、排队等。

2.4、熔断降级:

xml 复制代码
降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳
定,最终会导致请求发生堆积。这个问题和 Hystrix 里面描述的问题是一样的。

Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的
时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。




Sentinel和Hystrix在限制手段上,采取了不一样的方法:

1、Hystrix通过线程池的方式:
Hystrix 通过线程池的方式,来对依赖进行了隔离。

2、Sentinel 对这个问题采取了两种手段:
a、通过并发线程数进行限制
b、通过响应时间对资源进行降级

2.5、系统负载保护:

xml 复制代码
Sentinel 同时对系统的维度提供保护。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求
进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。
如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。


针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之
内处理最多的请求。

二、Sentinel控制台安装

xml 复制代码
Sentinel分为两个部分:
1、核心库
2、控制台dashboard,基于Springboot开发,打包后可直接运行,不需要Tomcat等额外的容器

下载:

xml 复制代码
https://github.com/alibaba/Sentinel/releases

运行:

xml 复制代码
Sentinel Dashboard默认的端口是8080

java -jar sentinel-dashboard-1.7.0.jar

访问:

xml 复制代码
http://localhost:8080/

账号:sentinel  密码:sentinel

三、Sentinel案例

3.1、初始化演示工程

创建cloudalibaba-sentinel-service-8401

xml 复制代码
<parent>
    <artifactId>cloud_2020</artifactId>
    <groupId>com.lee.springcloud</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>cloudalibaba-sentinel-service-8401</artifactId>

POM

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-sentinel-service-8401</artifactId>


    <dependencies>

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

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

        <dependency>
            <groupId>com.lee.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>

</project>

application.yml

yaml 复制代码
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: '*'

主启动类:

java 复制代码
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelMain8401 {

    public static void main(String[] args) {
        SpringApplication.run(SentinelMain8401.class,args);
    }
}

业务类:

java 复制代码
@Slf4j
@RestController
public class FlowLimitController {

    @GetMapping("/testA")
    public String testA(){
        return "=======>testA";
    }

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

测试:

xml 复制代码
1、启动nacos server、Sentinel-Dashboard、CloudAlibaba-sentinel-service-8401
2、查看sentinel控制台 http://localhost:8080/

结果:空空如也
(因为:Sentinel采用的是懒加载模式)
3、浏览:http://localhost:8401/testA和http://localhost:8401/testB
4、再查看sentinel控制台 http://localhost:8080/

3.2、流控规则

xml 复制代码
资源新增流控,可以通过两个地方来添加:
1、簇点链路-点击"+流控"
2、流控规则-点击"新增流控规则"


阈值类型:
QPS:系统每秒钟处理完成的请求次数

(QPS是在请求进入应用之前就处理,线程数是在请求进入应用之后处理)

3.2.1、流控模式

xml 复制代码
流控3种模式分为:直接、关联、链路
3.2.1.1、直接

测试:

xml 复制代码
1、直接的配置如上
2、每秒访问一次http://localhost:8401/testA
	正常
3、每秒多次快速的访问http://localhost:8401/testA
    Blocked by Sentinel (flow limiting)
(当再次恢复每秒1次的访问频率时,又可正常访问)


思考:
访问频次不符合配置规则后直接显示sentinel的默认处理Blocked by Sentinel,我们如何自定义规则不匹配的情况呢?
(后续会介绍:@SentinelResource去做方法的兜底处理,类似Hystrix的@HystrixCommand)
3.2.1.2、关联
xml 复制代码
什么是关联?即当与testA相关的资源testB达到阈值时,就挂testA

测试:

xml 复制代码
1、配置规则如上
2、postman模拟并发密集的访问http://localhost:8401/testB
3、浏览器访问http://localhost:8401/testA

结果:Blocked by Sentinel (flow limiting)

(当对testB密集访问停止后,testA访问恢复正常)
3.2.1.3、链路

新增代码:service

java 复制代码
@Slf4j
@Service
public class FlowLimitService {

    @SentinelResource("findOrder")
    public String findOrder(){
        log.info("----order----");
        return "order";
    }
}

controller

java 复制代码
@Slf4j
@RestController
public class FlowLimitController {

    @Resource
    private FlowLimitService flowLimitService;

    @GetMapping("/testA")
    public String testA(){
        flowLimitService.findOrder();
        return "=======>testA";
    }

    @GetMapping("/testB")
    public String testB(){
        flowLimitService.findOrder();
        return "=======>testB";
    }
}

配置规则:

xml 复制代码
1、配置规则如上
2、快速访问http://localhost:8401/testA
结果:
###############################有待确认#############
快速访问http://localhost:8401/testB
结果:
###############################有待确认#############

3.2.2、流控效果

3.2.2.1、快速失败
xml 复制代码
源码:
com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

上面写流控模式的时候已经展示了效果,不符合配置的时候,快速做出对应的反映,如
3.2.2.2、Warn Up预热
xml 复制代码
源码:
com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

公式:
阈值除以coldFactor(默认为3),经过预热时长后才会达到阈值


限流-冷启动:
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲
的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启
动,预热)模式就是为了实现这个目的的。


应用场景:
秒杀系统在开启的瞬间,会有很多流量上来,会把系统打死,预热方式就是为了保护系统,可以慢慢的把流量放进来。
慢慢的把阈值增长到设定的阈值、
xml 复制代码
配置解读:
系统初始化的阈值为10/3约等于3,即阈值刚开始为3.然后过了5秒后阈值才逐渐升到10
3.2.2.3、排队等待
xml 复制代码
源码:
com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

匀速排队,阈值必须设置为QPS。

匀速排队会严格控制请求通过的时间间隔。也即是让请求以均匀的速度通过,对应的是漏桶算法。(类似消息队列)
xml 复制代码
配置解读:
/testB每秒1次请求,超过的话就排队等待。等待的超时时间是20000ms

3.3、降级规则

xml 复制代码
降级规则包括:RT、异常比例、异常数
3.3.1、RT平均响应时间
xml 复制代码
RT平均响应时间:
当1s内持续进入5个请求,对应时刻的平均响应时间均超过阈值,那么在接下的时间窗口之内,对这个方法的调用都会自动地
熔断(抛出DegradeException).

Sentinel的默认RT上限是4900ms,超出此阈值的都会算作4900ms。若需要变更此上线可以通过:
-Dcsp.sentinel.statistic.max.rt=xxx来配置

新增代码:

java 复制代码
@GetMapping("/testD")
public String testD()
{
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("testD 测试RT");
    return "=======>testD";
}

配置

测试:

xml 复制代码
如上JMeter创建1s中10个线程的访问去请求testD.

然后再浏览器上访问http://localhost:8401/testD
结果:
Blocked by Sentinel (flow limiting)

(配置中,我们希望200毫秒处理完这次任务,如果200毫秒还没处理完,在未来1s的时间窗口内,断路器打开,微服务不可用)

(当我们停止JMeter测试后,没这么大访问量了,断路器关闭,微服务恢复。)
3.3.2、异常比例
xml 复制代码
异常比例:
当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下来的时间窗口之
内,对这个方法的调用都会自动返回。

新增代码:

java 复制代码
@GetMapping("/testE")
public String testE()
{
    log.info("testE 测试RT");
    int age = 10/0;
    return "------testE";
}

配置规则:

测试:

xml 复制代码
JMeter压测testE,每秒10个线程

浏览器访问:http://localhost:8401/testE

结果:不是报错,而是Blocked by Sentinel (flow limiting)

结论:
开启JMeter后,直接高并发发送请求,多次调用达到了我们的配置条件。断路器开启,微服务不可用,不再报错error而是
进入服务降级
3.3.3、异常数
xml 复制代码
异常数:
当资源近1分钟的异常数目超过阈值之后进入熔断,这个统计时间窗口是分钟级别的,若timewindow小于60s,则结束熔断状
态后仍可能再次进入熔断状态

新增代码:

java 复制代码
@GetMapping("/testF")
public String testF()
{
    log.info("testF 测试异常数");
    int age = 10/0;
    return "------testF 测试异常数";
}

配置规则如下:

xml 复制代码
浏览器访问:http://localhost:8401/testF
前5次报错,走Error
之后报:Blocked by Sentinel (flow limiting)
61s后,服务熔断解除,走Error

3.4、热点KEY限流

3.4.1、普通配置
xml 复制代码
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限
制。

之前的case,限流出现问题后,都是Sentinel的默认提示:Blocked by Sentinel (flow limiting)

我们将使用@SentinelResource,类似Hystrix的@HystrixCommand。当方法出现问题后就找对应的兜底方法返回。

新增方法如下:

java 复制代码
@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";
}

配置规则如下:

测试:

xml 复制代码
快速访问:http://localhost:8401/testHotKey   结果:------testHotKey

快速访问:http://localhost:8401/testHotKey?p1=abc    结果:------deal_testHotKey,o(╥﹏╥)o

快速访问:http://localhost:8401/testHotKey?p2=123  结果:------testHotKey

快速访问:http://localhost:8401/testHotKey?p1=abc&p2=123 结果:------deal_testHotKey,o(╥﹏╥)o

如果不快速访问,达不到阈值时,也只会返回------testHotKey
3.4.2、参数例外:

我们有时候期望当P1参数为某个特殊值时,它的限流值和平时不一样,假如当P1==5时,期望它的阈值为200。

测试:

xml 复制代码
1、浏览器快速访问:http://localhost:8401/testHotKey?p1=5 返回:------testHotKey

2、浏览器快速访问:http://localhost:8401/testHotKey?p1=3 返回:------deal_testHotKey,o(╥﹏╥)o

注意:参数必须是基础数据类型

3.5、系统规则

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

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

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


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

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

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

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

3.6、@SentinelResource

xml 复制代码
@SentinelResource:
	类似Hystrix的@HystrixCommand,做兜底服务自定义

其中blockXXXX是处理:配置违规的
	@blockHandlerClass 处理类
	@blockHandler 处理类下的处理方法

fallback:处理异常,做服务降级的
exceptionToIgnore:忽略异常

【程序异常和违规同时发生优先执行blockHandler】
3.6.1、按资源名称限流

新增业务类RateLimitController:

java 复制代码
@Slf4j
@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 服务不可用");
    }

}

新增配置:

测试:

xml 复制代码
上述配置解析:
表示1秒钟内查询次数大于1,就限流跑到我们自定义的方法

1、1s访问一次:http://localhost:8401/byResource
结果:{"code":200,"message":"按资源名称限流测试OK","data":{"id":2020,"serial":"serial001"}}

2、1s内快速多次访问:http://localhost:8401/byResource
结果:{"code":444,"message":"com.alibaba.csp.sentinel.slots.block.flow.FlowException\t 服务不可用","data":null}

根据资源名称限流,会优先返回我们自定义的处理信息,没有自定义的返回Sentinel默认限流处理信息
3.6.2、按URL地址限流

新增方法:

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

public CommonResult byUrlHandler(BlockException exception){
    return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}

新增配置:

测试:

xml 复制代码
1、1s访问一次:http://localhost:8401/rateLimit/byUrl
结果:
{"code":200,"message":"按url限流测试OK","data":{"id":2020,"serial":"serial002"}}

2、1s内快速访问:http://localhost:8401/rateLimit/byUrl
结果:
Blocked by Sentinel (flow limiting)

根据url地址限流返回的是Sentinel自带的默认限流处理信息
3.6.3、优化

以上一个方法新增一个对应的处理限流方法,代码冗余且和业务混杂,我们可以将处理方法提出来做单独处理

新增一个handler:

java 复制代码
public class CustomerBlockHandler {
	
    //注意---这里必须是static且不能为private
    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");
    }

}

业务类新增方法:

java 复制代码
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
                  blockHandlerClass = CustomerBlockHandler.class,
                  blockHandler = "handlerException")
public CommonResult customerBlockHandler()
{
    return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
}

新增对应的配置,然后测试:

xml 复制代码
1s内多次快速的访问:http://localhost:8401/rateLimit/customerBlockHandler

结果:
{"code":4444,"message":"按客戶自定义,global handlerException----1","data":null}

3.7、Sentinel与Ribbon和Feign的整合

3.7.1、Sentinel与Ribbon

3.7.1.1、创建cloudalibaba-provider-payment-9004和9005
xml 复制代码
<parent>
    <artifactId>cloud_2020</artifactId>
    <groupId>com.lee.springcloud</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-provider-payment-9004</artifactId>

pom

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloudalibaba-provider-payment-9004</artifactId>

    <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.lee.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>
</project>

application.yml

yaml 复制代码
server:
  port: 9004

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

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

主启动类

java 复制代码
@SpringBootApplication
@EnableDiscoveryClient//服务发现
public class PaymentMain9004 {

    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9004.class,args);
    }
}

业务类

java 复制代码
@Slf4j
@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,"aaa"));
        hashMap.put(2L,new Payment(2L,"bbb"));
        hashMap.put(3L,new Payment(3L,"ccc"));
    }

    @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.7.1.2、创建cloudalibaba-consumer-order-84
xml 复制代码
<parent>
    <artifactId>cloud_2020</artifactId>
    <groupId>com.lee.springcloud</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-consumer-order-84</artifactId>

POM

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloudalibaba-consumer-order-84</artifactId>

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


</project>

application.yml

yaml 复制代码
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

主启动类

java 复制代码
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class ConsumerOrderNacos84 {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerOrderNacos84.class,args);
    }
}

配置类

java 复制代码
@Configuration
public class ApplicationContextConfig {

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

}

业务类

java 复制代码
@Slf4j
@RestController
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);
    }

}
3.7.1.3、测试:
xml 复制代码
1、启动nacos-server、sentinel-server、cloudalibaba-provider-payment-9004、9005、
cloudalibaba-consumer-order-84

#############测试一############
2、浏览器访问:http://localhost:84/consumer/fallback/1

结果:
{"code":200,"message":"from mysql,serverPort:  9004","data":{"id":1,"serial":"aaa"}}
{"code":200,"message":"from mysql,serverPort:  9005","data":{"id":1,"serial":"aaa"}}
两个结果来回切换

3、浏览器访问:http://localhost:84/consumer/fallback/4

结果:
Whitelabel Error Page

#############测试二##############
4、consumer-84业务类放开注解@SentinelResource(value = "fallback",fallback = "handlerFallback")//fallback只负责业务异常

5、浏览器访问:http://localhost:84/consumer/fallback/4

结果:
{"code":444,"message":"兜底异常handlerFallback,exception内容  IllegalArgumentException,非法参数异常....","data":{"id":4,"serial":"null"}}

##############测试三############
6、consumer-84业务类放开注解@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规

7、且sentinel控制台新增fallback的降级策略

新增降级:
	资源名:fallback 策略:异常数	异常数:2 时间窗口:2

8、浏览器访问:http://localhost:84/consumer/fallback/2

结果:正常

浏览器访问:http://localhost:84/consumer/fallback/4

结果:Whitelabel Error Page

快速多次访问:http://localhost:84/consumer/fallback/4

结果:{"code":445,"message":"blockHandler-sentinel限流,无此流水: blockException  null","data":{"id":4,"serial":"null"}}

##################测试四############
9、consumer-84业务类放开注解@SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler",exceptionsToIgnore = {IllegalArgumentException.class})

10、浏览器访问:http://localhost:84/consumer/fallback/4

结果:Whitelabel Error Page

浏览器访问:http://localhost:84/consumer/fallback/5

结果:{"code":444,"message":"兜底异常handlerFallback,exception内容  NullPointerException,该ID没有对应记录,空指针异常","data":{"id":5,"serial":"null"}}


结论:

@blockHandlerClass 处理类
@blockHandler 处理类下的处理方法
fallback:处理异常,做服务降级的
exceptionToIgnore:忽略异常

3.7.2、Sentinel与OpenFeign

修改module:cloudalibaba-consumer-order-84

POM新增(上面创建的时候就已增加过了)

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

yml新增:

yaml 复制代码
#对Feign的支持
feign:
  sentinel:
    enabled: true

新增feign对应的接口:paymentService

java 复制代码
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {

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

}

新增feign接口的对应的服务降级类:PaymentFallbackService

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

新增controller:OrderController

java 复制代码
@Slf4j
@RestController
public class OrderController {

    // OpenFeign
    @Resource
    private PaymentService paymentService;

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


}

主启动类新增注解:

java 复制代码
@EnableFeignClients

测试:

xml 复制代码
1、启动nacos-server、sentinel-server、cloudalibaba-provider-payment-9004、9005、
cloudalibaba-consumer-order-84

2、浏览器访问:http://localhost:84/consumer/feign/paymentSQL/5

结果:
{"code":200,"message":"from mysql,serverPort:  9005","data":null}


4、此时关闭cloudalibaba-provider-payment-9004、9005两个微服务,重新访问

结果:
{"code":44444,"message":"服务降级返回,---PaymentFallbackService","data":{"id":5,"serial":"errorSerial"}}

3.8、Sentinel规则持久化

blic CommonResult paymentSQL(@PathVariable("id") Long id);

}

复制代码
新增feign接口的对应的服务降级类:PaymentFallbackService 

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

新增controller:OrderController

java 复制代码
@Slf4j
@RestController
public class OrderController {

    // OpenFeign
    @Resource
    private PaymentService paymentService;

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


}

主启动类新增注解:

java 复制代码
@EnableFeignClients

测试:

xml 复制代码
1、启动nacos-server、sentinel-server、cloudalibaba-provider-payment-9004、9005、
cloudalibaba-consumer-order-84

2、浏览器访问:http://localhost:84/consumer/feign/paymentSQL/5

结果:
{"code":200,"message":"from mysql,serverPort:  9005","data":null}


4、此时关闭cloudalibaba-provider-payment-9004、9005两个微服务,重新访问

结果:
{"code":44444,"message":"服务降级返回,---PaymentFallbackService","data":{"id":5,"serial":"errorSerial"}}

3.8、Sentinel规则持久化

//TODO 以后再单独列出文章来写

相关推荐
先睡3 小时前
Redis的缓存击穿和缓存雪崩
redis·spring·缓存
Bug退退退1238 小时前
RabbitMQ 高级特性之死信队列
java·分布式·spring·rabbitmq
guojl14 小时前
RestTemplate使用手册
spring cloud·微服务
guojl14 小时前
RestTemplate原理分析
spring cloud·微服务
booooooty14 小时前
基于Spring AI Alibaba的多智能体RAG应用
java·人工智能·spring·多智能体·rag·spring ai·ai alibaba
极光雨雨14 小时前
Spring Bean 控制销毁顺序的方法总结
java·spring
Ken_111514 小时前
SpringCloud系列(51)--SpringCloud Stream之使用分组解决消息重复消费问题
spring cloud
Spirit_NKlaus14 小时前
解决HttpServletRequest无法获取@RequestBody修饰的参数
java·spring boot·spring
lwb_011815 小时前
SpringCloud——Gateway新一代网关
spring·spring cloud·gateway
lxsy17 小时前
spring-ai-alibaba 1.0.0.2 学习(七)——集成阿里云百炼平台知识库
学习·spring·阿里云·spring-ai·ai-alibaba