代码编织梦想

SpringCloud Alibaba Sentinel

Sentinel 基础

官网

1 Github: https://github.com/alibaba/Sentinel

2 快速开始: https://sentinelguard.io/zh-cn/docs/quick-start.html

3 中文: https://github.com/alibaba/Sentinel/wiki/介绍

4 使用手册:
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel
image.png

Sentinel 概述

  1. Sentinel 是什么?

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

  1. Sentinel 的主要特性

image.png
梳理: sentinel 可以完成的功能: 绿色方框列出的部分

  1. Sentinel 的开源生态

image.png

一句话: Sentinel: 分布式系统的流量防卫兵, 保护你的微服务

Sentinel 核心功能

流量控制

  • 拿旅游景点举个示例,每个旅游景点通常都会有最大的接待量,不可能无限制的放游
    客进入,比如长城每天只卖八万张票,超过八万的游客,无法买票进入,因为如果超过
    八万人,景点的工作人员可能就忙不过来,过于拥挤的景点也会影响游客的体验和心情,
    并且还会有安全隐患;只卖N 张票,这就是一种限流的手段

示意图

image.png

熔断降级

  • 在调用系统的时候,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积,如下图

image.png

解读

熔断降级可以解决这个问题,所谓的熔断降级就是当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障

系统负载保护

根据系统能够处理的请求,和允许进来的请求,来做平衡,追求的目标是在系统不被拖垮的情况下, 提高系统的吞吐率

消息削峰填谷

某瞬时来了大流量的请求, 而如果此时要处理所有请求,很可能会导致系统负载过高,影响稳定性。但其实可能后面几秒之内都没有消息投递,若直接把多余的消息丢掉则没有充分利用系统处理消息的能力

Sentinel 的Rate Limiter 模式能在某一段时间间隔内以匀速方式处理这样的请求, 充分利用系统的处理能力, 也就是削峰填谷, 保证资源的稳定性

Sentinel 两个组成部分

  1. 核心库:(Java 客户端)不依赖任何框架/库,能够运行在所有Java 运行时环境, 对Spring Cloud有较好的支持

  2. 控制台:(Dashboard)基于Spring Boot 开发,打包后可以直接运行, 不需要额外的Tomcat 等应用容器

Sentinel 控制台

需求分析/图解

  1. 需求: 搭建Sentinel 控制台,用于显示各个微服务的使用情况

image.png

  1. 下载 https://github.com/alibaba/Sentinel/releases/tag/v1.8.0

image.png

运行

1 指令: java -jar sentinel-dashboard-1.8.0.jar

  1. 注意: Sentinel 控制台默认端口是8080

访问

1 浏览器: http://localhost:8080
2 控制台页面

浏览器输入: http://localhost:8080 , 用户/密码都是sentinel

image.png

  1. 登录成功后的页面, 目前是空的,因为sentinel 还没有进行流量监控

image.png

注意事项和细节

1、更改Sentinel 控制台的端口

java -jar sentinel-dashboard-1.8.0.jar --server.port=9999

Sentinel 监控微服务

需求分析/图解

  1. 需求: 使用Sentinel 控制台对member-service-nacos-provider-10004 微服务进行实时监控

示意图

image.png

  1. 当调用了member-service-nacos-provider-10004 微服务时, 可以监控到请求的url/QPS/响应时间/流量

代码/配置实现

修改member-service-nacos-provider-10004 的pom.xml

image.png
引入alibaba-sentinel

<!--引入alibaba-sentinel starter 即场景启动器 这里我们使用版本仲裁-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

       <!--引入nacos-starter nacos的场景启动器starter -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
        </dependency>

修改member-service-nacos-provider-10004 的application.yml

server:
  port: 10004

spring:
  application:
    name: member-service-nacos-provider #配置应用的名称
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/e_commerce_center_db?useSSL=true&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 自己的密码
  #配置nacos
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos Server的地址
    sentinel:
      transport:
        dashboard: localhost:8080 #指定sentinel控制台的地址
        port: 8719 #指定端口

解读transport.port

  1. transport.port 端口配置会在被监控的微服务对应主机上启动 Http Server

  2. 该Http Server 会与Sentinel控制台进行交互

  3. 比如sentinel 控制台添加了一个限流规则, 会把规则数据push 给这个Http Server 接收 Http Server 再将这个规则注册到Sentinel 中

  4. 简单的讲: transport.port 指定被监控的微服务应用于sentinel 控制台交互的端口.

  5. 默认端口是 8719, 假如被占用了, 就会自动的从8719开始依次+1扫描,直到找到一个没有被占用的端口

成功启动后, netstat -anb 可以查看到该端口

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 浏览器: localhost:10004/member/get/1

5 Sentinel 控制台监控页面

浏览器输入: http://localhost:10004/member/get/1

image.png
进入到Sentinel 查看实时监控效果, http://localhost:8080/#/dashboard

注意事项和细节

1 QPS: Queries Per Second(每秒查询率),是服务器每秒响应的查询次数
2 Sentinel 采用的是懒加载, 只有调用了某个接口/服务,才能看到监控数据

Sentinel 流量控制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X5vFTMk6-1685229425216)(<转存失败,建议直接上传图片文件 …/%E5%9B%BE%E7%89%87/image-20230526105400291.png>)]

对上图的解读

资源名∶唯一名称,默认请求路径
针对来源∶Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)

阈值类型/单机阈值∶
QPS(每秒钟的请求数量)∶当调用该api 的QPS 达到阈值的时候,进行限流
线程数∶当调用该api 的线程数达到阈值的时候,进行限流

解读QPS 和线程数的区别,

比如QPS 和线程我们都设置阈值为1
(1) 对QPS 而言, 如果在1 秒内, 客户端发出了2 次请求, 就达到阈值, 从而限流

(2) 对线程数而言, 如果在1 秒内, 客户端发出了2 次请求, 不一定达到线程限制的阈值,为什么呢? 假设我们1次请求后台会创建一个线程, 但是这个请求完成时间是0.1 秒(可以视为该请求对应的线程存活0.1 秒), 所以当客户端第2 次请求时(比如客户端是在0.3 秒发出的), 这时第1 个请求的线程就已经结束了, 因此就没有达到线程的阈值, 也不会限流就相当于处理的速度很快第二个线程创建的时候第一个线程已经结束了

(3) 可以这样理解, 如果1个请求对应的线程 平均执行时间为0.1 那么在1s内可以发送10次请求, 就相当于 线程阈值为1 和 QPS 阈值为10

相当于 1秒内发出11次请求 每个线程的执行时间为0.1 就不满足 1一个线程了 因为第10个线程还没有结束11个就来了所以11给会被失败

是否集群∶不需要集群

流控模式∶

​ 直接∶ api 达到限流条件时,直接限流

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

​ 链路∶ 当从某个接口过来的资源达到限流条件时,开启限流

流控效果∶

​ 快速失败∶直接失败,抛异常

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

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

流量控制实例-QPS

需求分析/图解

  1. 需求: 通过Sentinel 实现流量控制

  2. 当调用member-service-nacos-provider-10004 的/member/get/ 接口/API 时,限制1秒内最多访问1 次,否则直接失败,抛异常.

配置实现步骤

  1. 为/member/get/1 增加流控规则

image.png

  1. 在流控规则菜单,可以看到新增的流控规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 浏览器: localhost:10004/member/get/1

5 Sentinel 控制台监控页面
image.png

浏览器输入: http://localhost:10004/member/get/1 , 1 秒钟内访问次数超过1 次, 页面出现错误提示
image.png

注意事项和细节

  1. 流量规则改动,实时生效,不需重启微服务, Sentine 控制台
  2. 在sentinel 配置流量规则时, 如何配置通配符问题, 比如/member/get/1/member/get/2 统一使用一个规则
方案1: 在sentinel中/member/get?id=1 和/member/get?id=2 被统一认为是/member/get 所以只要对/member/get 限流就OK了

image.png

方案2: URL资源清洗

可以通过UrlCleaner 接口来实现资源清洗,也就是对于/member/get/{id}这个URL,
我们可以统一归集到/member/get/*资源下,具体配置代码如下,实现UrlCleaner 接口,并重写clean 方法即可

@Component
public class CustomUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String originUrl) { //资源清理

        /**
         * public static boolean isBlank(String str) {
         *         int strLen;
         *         if (str != null && (strLen = str.length()) != 0) {
         *             for(int i = 0; i < strLen; ++i) {//遍历
         *                 if (!Character.isWhitespace(str.charAt(i))) {
         *                     return false;
         *                 }
         *             }
         *
         *             return true;
         *         } else {
         *             return true;
         *         }
         *     }
         */
        //isBlank方法就是判断 originUrl!=null && 有长度 && originUrl不是全部为 空格
        if(StringUtils.isBlank(originUrl)) {
            return originUrl;
        }
        if(originUrl.startsWith("/member/get")) {//如果得到url是以/member/get开头,进行处理
            //解读
            //1. 如果请求的接口 是 /member/get 开头的, 比如 /member/get/1 , /member/get/10...
            //2. 给sentinel 放回资源名为 /member/get/*
            //3. 在sentinel 对 /member/get/* 添加流控规则即可
            return "/member/get/*";
        }
        return originUrl;
    }
}

  1. 如果sentinel 流控规则没有持久化,当我们重启调用API 所在微服务模块后,规则会丢失,需要重新加入 这个开不开不影响学习上面的知识 后面详细讲解

流量控制实例-线程数

需求分析/图解

  1. 需求: 通过Sentinel 实现流量控制
  2. 当调用member-service-nacos-provider-10004 的/member/get/* 接口/API 时,限制
    只有一个工作线程,否则直接失败,抛异常.

配置实现步骤

  1. 为/member/get/* 增加流控规则

image.png

image.png

  1. 在流控规则菜单,可以看到新增的流控规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 浏览器: localhost:10004/member/get/1

5 结果页面

浏览器输入: http://localhost:10004/member/get/1 , 快速刷新, 页面显示正常(原因是服务执行时间很短,刷新下一次的时候,启动的工作线程,已经完成)

image.png

为了看到效果,我们修改下controller/MemberController.java

    @GetMapping("/member/get/{id}")
    public Result getMemberById(@PathVariable("id") Long id) {
        //模拟超时, 让线程休眠1s
        try {
           TimeUnit.MILLISECONDS.sleep(2000);
        } catch (InterruptedException e) {
           e.printStackTrace();
        }

        //输出线程的情况
        log.info("enter 10004 getMemberById 当前线程id={} 时间={}",
                Thread.currentThread().getId(), new Date());

        Member member = memberService.queryMemberById(id);

        //使用Result把查询到的结果返回
        if (member != null) {
          
            return Result.success("查询会员成功 member-service-nacos-provider-10004", member);
        } else {
            return Result.error("402", "ID= " + id + "不存在");
        }

    }

重启member-service-nacos-provider-10004 , 注意需要重新加入流控规则.

浏览器输入: http://localhost:10004/member/get/1 , 快速刷新, 页面出现异常

image.png

注意事项和细节

  1. 当我们请求一次微服务的API 接口时,后台会启动一个线程[演示下]

image.png

  1. 阈值类型QPS 和线程数的区别讨论

    线程阈值为1的时候

    如果一个线程平均执行时间为0.05秒,就说明在1秒钟,可以执行20次(相当于QPS为20)

    如果一个线程平均执行时间为1秒, 说明1秒钟,可以执行1次数(相当于QPS为1)

    如果一个线程平均执行时间为2秒, 说明在2秒钟内,只能执行1次请求. 不然拒绝

流量控制实例-关联

关联的含义

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

需求分析/图解

  1. 需求: 通过Sentinel 实现流量控制

  2. 当调用member-service-nacos-provider-10004 的/t2 API 接口时,如果QPS 超过1,这时调用/t1 API 接口直接接失败,抛异常. /t2 是关联的资源, 限流的资源是
    /t1

    @GetMapping("/t1")
    public Result t1() {
        return Result.success("t1()执行..");
    }

    @GetMapping("/t2")
    public Result t2() {
 
        //输出线程信息
        log.info("执行t2() 线程id={}", Thread.currentThread().getId());
        return Result.success("t2()执行..");
    }

配置实现步骤

  1. 为/t1 增加流控规则

image.png
image.png

  1. 在流控规则菜单,可以看到新增的流控规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 Postman 模拟高并发访问/t2

  1. 创建新的http request

image.png

  1. 保存request 到一个新的collection 中

image.png

image.png

image.png
3. 设置run collection 参数, 并运行

image.png

image.png

image.png
4. 浏览器访问: http://localhost:10004/t1

image.png

注意事项和细节

在postman 执行高并发访问/t2 没有结束时, 去访问/t1 才能看到流控异常出现

流量控制实例-Warm up

Warm up 介绍

  1. 概述
    当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
    这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等
  2. 一张图

image.png

梳理

​ 通常冷启动的过程系统允许通过的QPS 曲线图(上图)

​ 默认coldFactor 为3,即请求QPS 从threshold / 3 开始,经预热时长逐渐升至设定的QPS 阈值

​ 这里的threshold 就是最终要达到的QPS阈值.

文档:https://github.com/alibaba/Sentinel/wiki/限流---冷启动
默认coldFactor 为3,即请求QPS 从threshold / 3 开始,经预热时长逐渐升至设定的QPS 阈值

Warm up 称为冷启动/预热

应用场景: 秒杀在开启瞬间,大流量很容易造成冲垮系统,Warmup 可慢慢的把流量放入,最终将阀值增长到设置阀值

需求分析/图解
  1. 需求: 通过Sentinel 实现流量控制,演示Warm up
  2. 调用member-service-nacos-provider-10004 的/t2 API 接口,将QPS 设置为9, 设置Warm up 值为3
  3. 含义为请求/t2 的QPS 从threshold / 3( 9 /3 = 3) 开始,经预热时长(3 秒)逐渐升至设定的QPS 阈值(9)
  4. 为什么是9 / 3, 这个是3 就是默认冷启动启动加载因子coldFactor=3
  5. 测试预期效果: 在前3 秒,如果访问/t2 的QPS 超过3, 会直接报错,在3 秒后访问/t2 的QPS 超过3, 小于等于9, 是正常访问

配置实现步骤

  1. 为/t2 增加流控规则

image.png

image.png

  1. 在流控规则菜单,可以看到新增的流控规则

image.png

测试

1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: localhost:10004/t2

浏览器访问http://localhost:10004/t2 快速刷新页面,在前3 秒,会出现流控异常,后3 秒就正常了(如果你刷新非常快QPS>9 , 仍然会出现流控异常)

image.png

注意事项和细节

测试Warm up 效果不是很好测,如果出不来可以尝试,调整流控规则: 比如QPS 为11, Warmup 预热时间6 秒
如果请求停止(即: 一段时间没有达到阈值), Warm up 过程将重复, 可以理解是一个弹性过程

流量控制实例-排队

排队介绍

  1. 排队方式:这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法

  2. 一张图

image.png

  1. 这种方式主要用于处理间隔性突发的流量,例如消息队列。比如这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。- 类似前面说的削峰填谷
  2. 匀速排队,阈值必须设置为QPS

需求分析/图解

  1. 需求: 通过Sentinel 实现流量控制-排队
  2. 调用member-service-nacos-provider-10004 的/t2 API 接口,将QPS 设置为1
  3. 当调用/t2 的QPS 超过1 时,不拒绝请求,而是排队等待, 依次执行
  4. 当等待时间超过10 秒,则为等待超时.

修改业务类

  1. 为了测试看到效果,修改controller/MemberController.java
    @GetMapping("/t1")
    public Result t1() {
        return Result.success("t1()执行..");
    }

    @GetMapping("/t2")
    public Result t2() {
        //让线程休眠1s, 模拟执行时间为1s => 当多少个请求就会造成超时
        try {
            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //输出线程信息
        log.info("执行t2() 线程id={}", Thread.currentThread().getId());
        return Result.success("t2()执行..");
    }

配置实现步骤

  1. 为/t2 增加流控规则

image.png

image.png

  1. 在流控规则菜单,可以看到新增的流控规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 浏览器: localhost:10004/t2

浏览器访问http://localhost:10004/t2 快速刷新页面9 次,观察前台/后台输出的情况

image.png

输出结果分析
没有报错误

后台请求排队执行,每隔1s 匀速执行

  1. 浏览器访问http://localhost:10004/t2 快速刷新页面20 次,当请求等待时间超过10S,仍然出现流控异常

image.png

Sentinel 熔断降级

线程堆积引出熔断降级

  1. 一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方API等[架构图]。

  2. 例如,支付的时候,可能需要远程调用银联提供的API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用

  3. 这时,我们对不稳定的服务进行熔断降级,让其快速返回结果,不要造成线程堆积

文档地址: https://sentinelguard.io/zh-cn/docs/circuit-breaking.html

基本介绍

image.png

解读上图:

​ 现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。

​ 链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。

​ 因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩

熔断,降级,限流三者的关系

​-熔断强调的是服务之间的调用能实现自我恢复的状态

​-限流是从系统的流量入口考虑, 从进入的流量上进行限制, 达到保护系统的作用

​-降级, 是从系统业务的维度考虑,流量大了或者频繁异常, 可以牺牲一些非核心业务,保护核心流程正常使用

梳理:

-熔断是降级方式的一种

-降级又是限流的一种方式

-三者都是为了通过一定的方式在流量过大或者出现异常时, 保护系统的手段

熔断策略

慢调用比例

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

image.png

异常比列

1、异常比例(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断

2、经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)

3、若接下来的一个请求成功完成(没有错误)则结束熔断, 否则会再次被熔断

4、异常比率的阈值范围是[0.0, 1.0],代表0% - 100%

5、配置参数

image.png

工作示意图

image.png

异常数

1、异常数(ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断

2、经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)

3、若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断

4、配置参考

image.png

熔断降级实例-慢调用比例

  1. 需求: 通过Sentinel 实现熔断降级控制-慢调用比例
  2. 当调用member-service-nacos-provider-10004 的/t3 API 接口时,如果在1s 内持续进入了5 个请求,并且请求的平均响应时间超过200ms, 那么就在未来10 秒钟内,断路器打开, 让/t3 API 接口微服务不可用
  3. 后面对/t3 API 接口访问降到1S 内1 个请求,降低访问量了,断路器关闭,微服务恢复

修改业务类

  1. 修改controller/MemberController.java 增加方法t3()
    @GetMapping("/t3")
    public Result t3() {
        //让线程休眠300ms毫秒, 模拟执行时间
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Result.success("t3()执行...");
    }

配置实现步骤

  1. 为/t3 增加降级规则

image.png

image.png

  1. 在流控规则菜单,可以看到新增的降级规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 Postman 测试

​ 4.1先创建collection , 也可以在已经存在的collection 进行修改

image.png

image.png

image.png

4.2点击Run sentinel

image.png

4.3 浏览器访问: http://localhost:10004/t3

image.png

4.4. 停止Postman

image.png

4.5. 浏览器访问: http://localhost:10004/t3 , 结果正常了(需要在停止Postman 10s 后)

image.png

注意事项和细节

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

2 熔断时间过后,关闭断路器,访问恢复正常

熔断降级实例-异常比例

需求分析/图解

  1. 需求: 通过Sentinel 实现熔断降级控制
    2. 当调用member-service-nacos-provider-10004 的/t4 API 接口时,当资源的每秒请求量>=5, 并且每秒异常总数占通过量的比值超过20%(即异常比例到20%), 断路器打开(即:进入降级状态), 让/t4 API 接口微服务不可用
  2. 当对/t4 API 接口访问降到1S 内1 个请求,降低访问量了,断路器关闭,5 秒后微服务恢复

修改业务类

  1. 修改controller/MemberController.java 增加方法t4()
    @GetMapping("/t4")
    public Result t4() {
        //设计异常比例达到50% > 20%
        if (++num % 2 == 0) {
            //制造一个异常
            System.out.println(3 / 0);
        }
        log.info("熔断降级测试[异常比例] 执行t4() 线程id={}",
                Thread.currentThread().getId());
        return Result.success("t4()执行...");
    }

配置实现步骤

  1. 为/t4 增加降级规则

image.png

image.png

  1. 在流控规则菜单,可以看到新增的降级规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 Postman 测试

  1. 先创建给collection , 也可以在已经存在的collection 进行修改, 一定确保更新成功.

image.png

image.png

  1. 点击Run sentinel

image.png

  1. 浏览器访问: http://localhost:10004/t4

image.png

  1. 停止Postman

image.png

  1. 浏览器访问: http://localhost:10004/t4 , 结果正常了(一次返回异常,一次返回正确结果)

image.png

注意事项和细节

1 当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值,资源进入降级状态, 需要两个条件都满足

2 测试时,如果熔断降级和恢复服务两个状态切换不明显,将熔断时间窗口值调整大一点比如60, 就OK 了

熔断降级实例-异常数

需求分析/图解

  1. 需求: 通过Sentinel 实现熔断降级控制

  2. 当调用member-service-nacos-provider-10004 的/t5 API 接口时,当资源的每分钟请求量>=5,并且每分钟异常总数>=5 , 断路器打开(即: 进入降级状态), 让/t5 API 接口微服务不可用

  3. 当熔断时间(比如20S)结束后,断路器关闭, 微服务恢复

修改业务类

1.修改controller/MemberController.java 增加方法t5()

解释为什么要怎么设计因为 第一次其实不能算 因为第一次的消耗要被sentinel监测到还没有加入限流的规则

所以从第二次加入规则之后开始算 2-3-4-5-6总共五次并且一分钟内 达到限流要求开始限流 然后就进入了20秒的熔断

然后第7次不会限流但是会抛异常 但是第8次 这个就要注意了

因为是一分钟内算所以说如果前面的4-5-6加20熔断时间在加7-8次在一分钟内 我们第8次一样的会被熔断 举一反三 如果第7次加上3-4-5-6 加20秒熔断在一分钟内同样的

   private static int num = 0;//执行的计数器-static静态
@GetMapping("/t5")
    public Result t5() {
        //解读
        //出现10次异常, 这里需要设置大于6, 需要留出几次做测试和加入簇点链路
        if (++num <= 10) {
            //制造一个异常
            System.out.println(3 / 0);
        }
        log.info("熔断降级测试[异常数] 执行t5() 线程id={}",
                Thread.currentThread().getId());
        return Result.success("t5()执行了...");
    }

配置实现步骤

1、为/t5 增加降级规则

image.png

2、在流控规则菜单,可以看到新增的降级规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 浏览器: http://localhost:10004/t5

http://localhost:10004/t5 , 访问5 次,出现5 次异常(1 分钟内完成)

image.png

5 次异常后,出现熔断降级

image.png

  1. 20S 后,再次访问http://localhost:10004/t5, 返回正常结果了

image.png

直到num大于10从就可以访问了

image.png

注意事项和细节

1 资源在1 分钟的异常数目超过阈值之后会进行熔断降级

2 异常数统计是分钟级别的,若设置的熔断时间窗口小于60s,则结束熔断状态后仍可能再进入熔断状态, 测试时,最好将熔断时间窗口设置超过60S

Sentinel 热点规则

一个问题引出热点key 限流

  1. 热点: 热点即经常访问的数据。很多时候我们希望统计热点数据中, 访问频次最高的Top K 数据,并对其访问进行限制。
  2. 比如某条新闻上热搜,在某段时间内高频访问, 为了防止系统雪崩, 可以对该条新闻进行热点限流

文档地址:https://github.com/alibaba/Sentinel/wiki/热点参数限流

基本介绍

image.png

解读上图:

  1. 热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流
  2. 热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效
  3. Sentinel 利用LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控https://blog.csdn.net/qq_34416331/article/details/106668747
  4. 热点参数限流支持集群模式

热点Key 限流-实例

需求分析/图解

  1. 需求: 通过Sentinel 实现热点Key 限流
  2. 对member-service-nacos-provider-10004 的/news?id=x&type=x API 接口进行热点限流
  3. 假定id=10 这一条新闻是当前的热点新闻, 当查询新闻时,对通常的id(非热点新闻)请求QPS 限定为2, 如果id=10 QPS 限定为100
  4. 如果访问超出了规定的QPS, 触发热点限流机制, 调用自定义的方法,给出提示信息.
  5. 当对/news?id=x&type=x API 接口降低访问量,QPS 达到规定范围, 服务恢复

修改业务类

  1. 修改controller/MemberController.java 增加方法queryNews()
/**
     * 解读
     * 1.@SentinelResource : 指定sentinel限流资源
     * 2.value = "news" 表示sentinel限流资源 名称,由程序员指定
     * 3. blockHandler = "newsBlockHandler": 当出现限流时,由newsBlockHandler方法进行处理
     */
    @GetMapping("/news")
    @SentinelResource(value = "news", blockHandler = "newsBlockHandler")
    public Result queryNews(@RequestParam(value = "id", required = false) String id,
                            @RequestParam(value = "type", required = false) String type) {
        //在实际开发中, 新闻应该到DB或者缓存获取,这里就模拟
        log.info("到DB 查询新闻");
        return Result.success("返回id=" + id + " 新闻 from DB");
    }

    //热点key限制/限流异常处理方法
    public Result newsBlockHandler(String id, String type,
                                   BlockException blockException) {

        return Result.success("查询id=" + id + " 新闻 触发热点key限流保护 sorry...");
    }

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

配置实现步骤

  1. 为资源news 增加热点规则, 注意不是/news

image.png

image.png

  1. 在热点参数限流规则菜单,可以看到新增规则

image.png

  1. 浏览器输入: http://localhost:10004/news?id=1&type=教育 , 如果QPS 没有超过2,则返回正确结果

image.png

  1. 如果QPS 超过2,则返回热点key 处理信息

image.png

独立设置热点

  1. 独立设置热点id=10 的QPS 阈值(即添加例外)

image.png

image.png
2. 浏览器输入: http://localhost:10004/news?id=10&type=教育 , 如果QPS 没有超过100,则返回正确结果

image.png

  1. 浏览器访问的id 不是10 的,仍然遵守QPS 不能超过2 的热点限制

image.png

注意事项和细节

1 热点参数类型是(byte/int/long/float/double/char/String)

2 热点参数值,可以配置多个

3 热点规则只对指定的参数生效(比如本实例对id 生效, 对type 不生效 就是说限制是跟你的状态没关系的)

系统规则

一个问题引出系统规则

  1. 如我们系统最大性能能抗100QPS, 如何分配/t1 /t2 的QPS?
  2. 方案1: /t1 分配QPS=50 /t2 分配QPS=50 , 问题, 如果/t1 当前QPS 达到50 , 而/t2 的QPS 才10, 会造成没有充分利用服务器性能.
  3. 方案2: /t1 分配QPS=100 /t2 分配QPS=100 , 问题, 容易造成系统没有流量保护,造成请求线程堆积,形成雪崩.
  4. 有没有对各个资源请求的QPS 弹性设置, 只要总数不超过系统最大QPS 的流量保护规则? ==> 系统规则

文档地址:https://github.com/alibaba/Sentinel/wiki/系统自适应限流

一句话: 系统规则作用, 在系统稳定的前提下,保持系统的吞吐量

基本介绍

示意图

image.png

解读上图:

  1. 系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;

  2. 反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间+ 最短处理时间

  3. 相当于 你在排队是第3名 处理人员是1分钟处理好一个人 所以排队时间为3分钟 处理你的业务需要一分钟这个就是最短处理时间那么你的处理总时间就是4分钟

系统规则

  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. 需求: 通过Sentinel 实现系统规则-入口QPS
  2. 对member-service-nacos-provider-10004 的所有API 接口进行流量保护,不管访问哪个API 接口, 系统入口总的QPS 不能大于2, 大于2,就进行限流控制
  3. 提示: 上面的QPS 是为了方便看效果, 设置的很小

配置实现步骤

  1. 增加入口QPS 系统规则

image.png

image.png

image.png

测试

1 启动Nacos Server 8848
2 启动Sentinel8080 控制台/Sentinel dashboard
3 启动member-service-nacos-provider-10004
4 浏览器: http://localhost:10004/t1

image.png

如果QPS 超过2, 打开断路器,返回流控信息(说明: 目的/t2 资源对应方法有休眠代码,所以使用/news?id=x&type=x 测试)

image.png

@SentinelResource

自定义全局限流处理类

需求分析/图解

  1. 先看前面的一段代码

    这个就是上面的Sentinel 热点规则 注意看我们的限制处理方法在本类中代码的耦合度高 阅读性差 不利于程序的扩展 @SentinelResource的作用就是解决这个需求将处理方法放到一个类中


    @GetMapping("/news")
    @SentinelResource(value = "news", blockHandler = "newsBlockHandler")
    public Result queryNews(@RequestParam(value = "id", required = false) String id,
                            @RequestParam(value = "type", required = false) String type) {
        //在实际开发中, 新闻应该到DB或者缓存获取,这里就模拟
        log.info("到DB 查询新闻");
        return Result.success("返回id=" + id + " 新闻 from DB");
    }

    //热点key限制/限流异常处理方法
    public Result newsBlockHandler(String id, String type,
                                   BlockException blockException) {

        return Result.success("查询id=" + id + " 新闻 触发热点key限流保护 sorry...");
    }

说明: 当配置的资源名news 触发限流机制时,会调用newsBlockHandler 方法

  1. 上面的处理方案存在一些问题
    每个@SentinelResource 对应一个异常处理方法,会造成方法很多异常处理方法和资源请求方法在一起,不利于业务逻辑的分离解决方案-> 自定义全局限流处理类.
  2. 需求: 请编写一个自定义全局限流处理类,完成对异常处理.

代码实现

  1. 修改controller/MemberController.java 增加方法t6()
    /**
     * 解读
     * value = "t6": SentinelResource 资源名
     * blockHandlerClass = CustomGlobalBlockHandler.class: 全局限流处理类
     * blockHandler = "handlerMethod1": 全局限流处理类的哪个方法,可以指定.
     */
    @GetMapping(value = "/t6")
    @SentinelResource(
            value = "t6",
            blockHandlerClass = CustomGlobalBlockHandler.class,
            blockHandler = "handlerMethod1")
    public Result t6() {
        log.info("执行t6() 线程id= " + Thread.currentThread().getId());
        return Result.success("200", "t6()执行成功");
    }
  1. 创建springcloud/handler/CustomGlobalBlockHandler.java

提醒, 创建的异常处理方法需要是static

/**
 * 1. CustomGlobalBlockHandler: 全局限流处理类
 * 2. 在CustomGlobalBlockHandler类中,可以编写限流处理方法,但是要求方法是static
 */
public class CustomGlobalBlockHandler {

    public static Result handlerMethod1(BlockException blockException) {
        return Result.error("400", "客户自定义异常/限流处理方法handlerMethod1() ");
    }
    public static Result handlerMethod2(BlockException blockException) {
        return Result.error("401", "客户自定义异常/限流处理方法handlerMethod2() ");
    }
}

配置实现步骤

  1. 为资源/t6 增加流控规则,方便测试

image.png

image.png
2. 在流控规则菜单,可以看到新增规则

image.png

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 浏览器: http://localhost:10004/t6

1. 浏览器输入: http://localhost:10004/t6 , 如果QPS 没有超过1, 返回正常结果

image.png

  1. 如果QPS 超过1, 断路器打开,返回自定义限流处理方法信息

image.png

fallback

看一段代码-引出fallback

  1. 修改member-service-nacos-provider-10004 的controller/MemberController.java 增加一段代码.
  2. 就是@SentinelResource的t6方法 我们模拟java异常看看会发生什么
    @GetMapping(value = "/t6")
    @SentinelResource(
            value = "t6",
            blockHandlerClass = CustomGlobalBlockHandler.class,
            blockHandler = "handlerMethod1"
    )
    public Result t6() {
        //假定: 当访问t6 资源次数是5 的倍数时,就出现了一个java 的异常
        if (++num % 5 == 0) {
            throw new RuntimeException("num 的值异常num= " + num);
        }
        log.info("执行t6() 线程id= " + Thread.currentThread().getId());
        return Result.success("200", "t6()执行成功");
    }
  1. 浏览器: http://localhost:10004/t6 , 看效果当num 为5 的整数时,返回的是error 页面,不友好.

image.png

  1. 怎么解决=> 使用fallback

基本介绍

1 blockHandler 只负责sentine 控制台配置违规

2 fallback 负责Java 异常/业务异常

需求分析/图解
  1. 需求: 请编写一个自定义全局fallback 处理类, 处理java 异常/业务异常
  2. 也就是解决前面我们提出的问题
代码实现

1.在member-service-nacos-provider-10004 创建/handler/CustomGlobalFallbackHandler.java

/**
 * CustomGlobalFallbackHandler: 全局fallback处理类
 * 在CustomGlobalFallbackHandler类中,可以去编写处理java异常/业务异常方法-static
 */

public class CustomGlobalFallbackHandler {

    public static Result fallbackHandlerMethod1(Throwable e) {
        return Result.error("402","java异常 信息=" + e.getMessage());
    }

    public static Result fallbackHandlerMethod2(Throwable e) {
        return Result.error("403","java异常 信息=" + e.getMessage());
    }
}

  1. 在member-service-nacos-provider-10004 修改controller/MemberController.java
    /**
     * value = "t6" 表示 sentinel限流资源的名字
     * blockHandlerClass = CustomGlobalBlockHandler.class : 全局限流处理类
     * blockHandler = "handlerMethod1": 指定使用全局限流处理类哪个方法,来处理限流信息
     * fallbackClass = CustomGlobalFallbackHandler.class: 全局fallback处理类
     * fallback = "fallbackHandlerMethod1": 指定使用全局fallback处理类哪个方法来处理java异常/业务异常
     */
    //这里我们使用全局限流处理类,显示限流信息
    @GetMapping("/t6")
    @SentinelResource(value = "t6",
            fallbackClass = CustomGlobalFallbackHandler.class,
            fallback = "fallbackHandlerMethod1",
            blockHandlerClass = CustomGlobalBlockHandler.class,
            blockHandler = "handlerMethod1"
                     )
    public Result t6() {
        //假定: 当访问t6资源次数是5的倍数时,就出现java异常
        if (++num % 5 == 0) {
            throw new NullPointerException("null指针异常 num=" + num);
        }
 

        log.info("执行t6() 线程id={}", Thread.currentThread().getId());
        return Result.success("200", "t6()执行OK~~");
    }
测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004

4 浏览器: http://localhost:10004/t6

浏览器输入: http://localhost:10004/t6 , 访问次数不是5 的倍数, 返回正常结果

image.png

  1. 浏览器输入: http://localhost:10004/t6 , 访问次数是5 的倍数, 返回fallback 指定方法信息

image.png

  1. 为资源/t6 增加流控规则,方便测试

image.png

image.png

  1. 在流控规则菜单,可以看到新增规则

image.png

  1. 浏览器输入: http://localhost:10004/t6 , 如果访问QPS 大于1 , 由blockHandler 指定的方法处理,访问次数是5 的倍数, 由fallback 指定方法处理, 其它情况返回正常的结果.

image.png

image.png

image.png

exceptionsToIgnore

如果希望忽略某个异常,可以使用exceptionsToIgnore

    /**
     * value = "t6" 表示 sentinel限流资源的名字
     * blockHandlerClass = CustomGlobalBlockHandler.class : 全局限流处理类
     * blockHandler = "handlerMethod1": 指定使用全局限流处理类哪个方法,来处理限流信息
     * fallbackClass = CustomGlobalFallbackHandler.class: 全局fallback处理类
     * fallback = "fallbackHandlerMethod1": 指定使用全局fallback处理类哪个方法来处理java异常/业务异常
     * exceptionsToIgnore = {RuntimeException.class}: 表示如果t6()抛出RuntimeException, 就使用系统默认方式处理
     */
    //这里我们使用全局限流处理类,显示限流信息
    @GetMapping("/t6")
    @SentinelResource(value = "t6",
            fallbackClass = CustomGlobalFallbackHandler.class,
            fallback = "fallbackHandlerMethod1",
            blockHandlerClass = CustomGlobalBlockHandler.class,
            blockHandler = "handlerMethod1",
                          //注意 这个之所以是NullPointerException.class的原因是RuntimeException异常包括NullPointerException如果写RuntimeException异常那么NullPointerException异常也会捕获到那就没意义了
            exceptionsToIgnore = {NullPointerException.class})
    public Result t6() {
        //假定: 当访问t6资源次数是5的倍数时,就出现java异常
        if (++num % 5 == 0) {
            throw new NullPointerException("null指针异常 num=" + num);
        }
        if (num % 6 == 0) {//当访问t6资源次数是6的倍数时,抛出 runtime异常
            throw new RuntimeException("RuntimeException num=" + num);
        }

        log.info("执行t6() 线程id={}", Thread.currentThread().getId());
        return Result.success("200", "t6()执行OK~~");
    }
  1. 浏览器输入: http://localhost:10004/t6 , 你会发现访问次数为5 的倍数时,不再调用fallback 指定方法处理

在Sentinel中t6增加流控 这时就有4种方法

image.png

  1. 正常执行

image.png

  1. 访问过快被blockHandler限流捕获到

image.png

  1. 这个是忽略的异常

image.png

  1. 这个是被fallback捕获的异常

image.png

接入Sentinel 的方式

10.8.4.1 代码方式(硬编码,侵入性强, 不推荐)

  1. 文档地址: https://github.com/alibaba/Sentinel/wiki/介绍
  2. 基本使用

image.png

image.png

image.png

注解方式(低侵入性, 前面用过, 推荐)

  1. 注解方式埋点不支持private 方法
    https://xue.baidu.com/okam/pages/strategy-tp/index?strategyId=136707206360879&source=natural
  2. @SentinelResource 用于定义资源,并提供可选的异常处理和fallback 配置项。

@SentinelResource 注解包含以下属性(我们再梳理一下)

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

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

  3. blockHandler / blockHandlerClass: blockHandler 对应处理BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其
    他类的函数,则可以指定blockHandlerClass 为对应的类的Class 对象,注意对应的函数必需为static 函数,否则无法解析。

  4. fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
    (1)返回值类型必须与原函数返回值类型一致;
    (2)方法参数列表需要和原函数一致,或者可以额外多一个Throwable 类型的参数用于接收对应的异常。
    (3)fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass 为对应的类的Class 对象,注意对应的函数必需为static 函数,否则无法解析。

  5. defaultFallback(since 1.6.0):默认的fallback 函数名称,可选项,通常用于通用的
    fallback 逻辑(即可以用于很多服务或方法)。默认fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了fallback 和defaultFallback,则只有fallback 会生效。defaultFallback 函数签名要求:
    (1)返回值类型必须与原函数返回值类型一致;
    (2)方法参数列表需要为空,或者可以额外多一个Throwable 类型的参数用于接收对应的异常。
    (3)defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass 为对应的类的Class 对象**,注意对应的函数必需为static 函数,否则无法解析。**

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

openFeign+sentinel 对远程调用熔断降级

示意图

image.png

测试

1 启动Nacos Server 8848

2 启动member-service-nacos-provider-10004/10006

3 启动member-service-nacos-consumer-80

4 浏览器: http://localhost/member/nacos/consumer/get/1

浏览器输入: http://localhost/member/nacos/consumer/get/1 , 目前是Ribbon+RestTemplate

image.png

image.png

服务消费者整合Openfeign

需求分析/图解

需求:在member-service-nacos-consumer-80 整合Openfeign 实现远程调用

image.png

代码+配置实现步骤

  1. 修改member-service-nacos-consumer-80 的pom.xml 加入openfeign 依赖
<!-- 引入openfeign -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在member-service-nacos-consumer-80 创建/springcloud/service/MemberOpenFeignService.java接口
@FeignClient(value = "member-service-nacos-provider")
public interface MemberOpenFeignService {
    /**
     * 解读
     * 1. 远程调用方式是 get
     * 2. 远程调用的url 为 http://member-service-nacos-provider/member/get/{id}
     * 3. member-service-nacos-provider是nacos注册中心服务名
     * 4. openfeign会根据负载均衡算法来决定调用的是 10004/10006,默认是轮询算法
     * 5. openfeign是通过接口方式调用服务
     */
    @GetMapping("/member/get/{id}")
    public Result getMemberById(@PathVariable("id") Long id);
}

  1. 修改controller/MemberNacosConsumerController.java , 增加方法
    //装配MemberOpenFeignService
    @Resource
    private MemberOpenFeignService memberOpenFeignService;

    //编写方法通过openfeign实现远程调用
    @GetMapping("/member/openfeign/consumer/get/{id}")
    public Result<Member> getMemberOpenfeignById(@PathVariable("id") Long id) {
        //这里我们使用openfeign接口方式远程调用
        log.info("调用方式是 openfeign..");
        return memberOpenFeignService.getMemberById(id);
    }
  1. 在member-service-nacos-consumer-80 的主启动类加入注解
@SpringBootApplication
@EnableDiscoveryClient //引入的是启动 nacos发现注解
@EnableFeignClients
public class MemberNacosConsumerApplication80 {
    public static void main(String[] args) {

        SpringApplication.run(MemberNacosConsumerApplication80.class,args);
    }
}

测试
1 启动Nacos Server 8848

2 启动member-service-nacos-provider-10004/10006

3 启动member-service-nacos-consumer-80

4 浏览器: http://localhost/member/openfeign/consumer/get/1

浏览器输入: http://localhost/member/openfeign/consumer/get/1 , 目前是Openfeign调用(负载均衡)

image.png

服务消费者整合Sentinel

需求分析/图解

需求:在member-service-nacos-consumer-80 整合Sentinel 能被Sentinel 监控

image.png

代码+配置实现步骤

  1. 修改member-service-nacos-consumer-80 的pom.xml 加入sentinel 依赖
<!-- 引入alibaba-sentinel -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  1. 修改member-service-nacos-consumer-80 的application.yml 配置sentinel
server:
  port: 80

spring:
  application:
    name: member-service-nacos-consumer-80
  #配置nacos
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #nacos server的地址
    sentinel:
      transport:
        dashboard: localhost:8080 #指定sentinel控制台地址(dash board)
        port: 8719 #设置端口默认是 8719, 如果该端口被占用, 就自动从 8791+1进行扫描, 直到找到一个没有占用的端口
#设置暴露所有的监控点
management:
  endpoints:
    web:
      exposure:
        include: '*'

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004/10006

4 启动member-service-nacos-consumer-80

5 浏览器: http://localhost/member/openfeign/consumer/get/1

  1. 目前是Openfeign调用(负载均衡)

image.png

  1. 登录sentinel 控制台: 可以看到已经监控到member-service-nacos-consumer

image.png

openFeign+sentinel 对远程调用熔断降级

需求分析/图解

  1. 需求/如图:在member-service-nacos-consumer-80 调用某个无效服务时,启动Sentinel的熔断降级机制, 能够快速返回响应,而不是使用默认的超时机制(因为超时机制容易线
    程堆积, 从而导致雪崩)

image.png

  1. 先测试一下,关闭10004/10006, 这时openfeign 去调用会怎么样? (返回time out)

image.png

  1. 还可以测试一下,让10004 服务对应的API 执行时间很长(比如休眠2 秒), 这时openfeign 去调用会怎么样?

image.png

代码+配置实现步骤

  1. 修改member-service-nacos-consumer-80 的service/MemberOpenFeignService.java接口, 加入fallback 的处理类
@FeignClient(value = "member-service-nacos-provider", fallback = MemberFeignFallbackService.class)
public interface MemberOpenFeignService {
    /**
     * 解读
     * 1. 远程调用方式是 get
     * 2. 远程调用的url 为 http://member-service-nacos-provider/member/get/{id}
     * 3. member-service-nacos-provider是nacos注册中心服务名
     * 4. openfeign会根据负载均衡算法来决定调用的是 10004/10006,默认是轮询算法
     * 5. openfeign是通过接口方式调用服务
     */
    @GetMapping("/member/get/{id}")
    public Result getMemberById(@PathVariable("id") Long id);
}

  1. 创建MemberFeignFallbackService.java
@Component
public class MemberFeignFallbackService
        implements MemberOpenFeignService {
    @Override
    public Result getMemberById(Long id) {
        return Result.error("500", "被调用服务异常, 熔断降级, 快速返回结果,防止线程堆积..");
    }
}

  1. 修改member-service-nacos-consumer-80 的application.yml , 加入openfeign 和sentinel 整合配置
server:
  port: 80

spring:
  application:
    name: member-service-nacos-consumer-80
  #配置nacos
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #nacos server的地址
    sentinel:
      transport:
        dashboard: localhost:8080 #指定sentinel控制台地址(dash board)
        port: 8719 #设置端口默认是 8719, 如果该端口被占用, 就自动从 8791+1进行扫描, 直到找到一个没有占用的端口
#设置暴露所有的监控点
management:
  endpoints:
    web:
      exposure:
        include: '*'
#openfeign和sentinel整合,必须配置
feign:
  sentinel:
    enabled: true

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 关闭member-service-nacos-provider-10004/10006

4 启动member-service-nacos-consumer-80

5 浏览器: http://localhost/member/openfeign/consumer/get/1

浏览器输入: <http://localhost/member/openfeign/consumer/get/1> , 目前是Openfeign调用(负载均衡)

image.png

注意事项和细节说明

  1. 因为member-service-nacos-consumer-80 已经被sentinel 监控,所以我们可以加入相关的流控规则, 比如为/member/openfeign/consumer/get/1 加入流控规则qps = 1

image.png

测试: 如果/member/openfeign/consumer/get/1 请求QPS 超过1, 会输出

image.png

QPS 没有超过1, 会被fallback 处理, 如图

image.png
2. 如果远程服务恢复正常, 又会正常调用

规则持久化

规则没有持久化的问题

1 如果sentinel 流控规则没有持久化,当重启调用API/接口所在微服务后,规则就会丢失,需要重新加入
2 解决方案:通过Nacos 进行持久化

规则持久化方案

  1. 阿里云Ahas[最方便/付费]

    官方文档:https://help.aliyun.com/product/87450.html?spm=5176.cnahas.0.0.78034bb7ef0y86

image.png

  1. 在Nacos Server 配置规则, 完成持久化-官方推荐

  2. 将规则持久化到本地文件, 定时同步

  3. 其它…

Nacos Server 配置中心-规则持久化实例

工作原理示意图

image.png

需求分析/图解

  1. 需求: 为member-service-nacos-consumer-80 微服务的/member/openfeign/consumer/get/1 API 接口添加流控规则QPS=1/快速失败.
  2. 要求将该流控规则加入到nacos server 配置中心,实现持久化

image.png

[
	{
		"resource":"/member/openfeign/consumer/get/1",
		"limitApp": "default",
		"grade": 1,
		"count": 1,
		"strategy": 0,
		"controlBehavior": 0,
		"clusterMode": false
	}
]

image.png

  1. 在Nacos Server 配置中心增加Sentinel 客户端/微服务模块的流控规则参数说明
  • resource∶资源名称;
  • limlitApp∶ 来源应用;
  • grade∶阈值类型,0表示线程数,1表示QPS;
  • count∶单机阈值;
  • strategy∶流控模式,0表示直接,1表示关联,2表示链路;
  • controlBehavior∶流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
  • clusterMode∶是否集群
  1. 修改member-service-nacos-consumer-80 的pom.xml, 加入加入sentinel 和nacos持久化整合依赖
<!-- 加入sentinel 和nacos 持久化整合依赖-->
<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
  1. 修改member-service-nacos-consumer-80 的application.yml , 配置该微服务从NacosServer 获取流控规则
server:
  port: 80

spring:
  application:
    name: member-service-nacos-consumer-80
  #配置nacos
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #nacos server的地址
    sentinel:
      transport:
        dashboard: localhost:8080 #指定sentinel控制台地址(dash board)
        port: 8719 #设置端口默认是 8719, 如果该端口被占用, 就自动从 8791+1进行扫描, 直到找到一个没有占用的端口
      datasource:
        ds1:
          #流控规则配置是从nacos server 配置中心获取
          nacos:
            server-addr: localhost:8848 #指定nacos server 配置中心地址
            dataId: member-service-nacos-consumer #nacos server 配置中心 dataId
            groupId: DEFAULT_GROUP #指定组[nacos server配置中心]
            data-type: json #指定配置流控规则的数据类型
            rule-type: flow #规则类型: 流控规则 表示一会看文档
#设置暴露所有的监控点
management:
  endpoints:
    web:
      exposure:
        include: '*'
#openfeign和sentinel整合,必须配置
feign:
  sentinel:
    enabled: true

测试

1 启动Nacos Server 8848

2 启动Sentinel8080 控制台/Sentinel dashboard

3 启动member-service-nacos-provider-10004/10006

4 启动member-service-nacos-consumer-80

5 浏览器: http://localhost/member/openfeign/consumer/get/1

  1. 浏览器输入: http://localhost/member/openfeign/consumer/get/1 , 目前是Openfeign调用(负载均衡), 而且流控规则已经生效了.

image.png
2. 注意看sentinel, 这个流控规则已经生成了.

image.png

  1. 查看Sentinel 控制台, 发现已经同步了流控规则

image.png

注意事项和细节

  1. 在nacos server 配置sentinel 流控规则的Data ID 也可以自己指定,比如写成wyx-id,只要在sentinel client/微服务的applicaion.yml 的datasource.ds1.nacos.dataId 的值保持一致即可
  2. 如图所示

image.png

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/apple_67445472/article/details/130909020