风在路上 风在路上
首页
导航站
  • Java-Se

    • Java基础
  • Java-Se进阶-多线程

    • 多线程
  • Java-Se进阶-java8新特性

    • java8新特性
  • Java-ee

    • JavaWeb
  • Java虚拟机

    • JVM
  • golang基础

    • golang基础
  • golang框架

    • gin
  • SQL 数据库

    • MySQL
  • NoSQL 数据库

    • Redis
    • ElasticSearch
    • MongoDB
  • ORM

    • MyBatis
    • MyBatis-Plus
  • Spring

    • Spring
  • SpringMVC

    • SpringMVC1
    • SpringMVC2
  • SpringCloud

    • SpringCloud
  • 中间件

    • RabbitMQ
    • Dubbo
  • 秒杀项目
  • Git
  • Linux
  • Docker
  • JWT
  • 面试
  • 刷题
开发问题😈
设计模式
关于💕
归档🕛
GitHub (opens new window)

风

摸鱼
首页
导航站
  • Java-Se

    • Java基础
  • Java-Se进阶-多线程

    • 多线程
  • Java-Se进阶-java8新特性

    • java8新特性
  • Java-ee

    • JavaWeb
  • Java虚拟机

    • JVM
  • golang基础

    • golang基础
  • golang框架

    • gin
  • SQL 数据库

    • MySQL
  • NoSQL 数据库

    • Redis
    • ElasticSearch
    • MongoDB
  • ORM

    • MyBatis
    • MyBatis-Plus
  • Spring

    • Spring
  • SpringMVC

    • SpringMVC1
    • SpringMVC2
  • SpringCloud

    • SpringCloud
  • 中间件

    • RabbitMQ
    • Dubbo
  • 秒杀项目
  • Git
  • Linux
  • Docker
  • JWT
  • 面试
  • 刷题
开发问题😈
设计模式
关于💕
归档🕛
GitHub (opens new window)
  • mybatis

  • mybatis-plus

  • Spring

  • SpringMvc

  • RabbitMQ

  • Dubbo

  • SpringCloud

    • SpringCloud知识体系
    • Eureka
    • ZookeeperAndConsul
    • Ribbn
    • OpenFeign
    • Hystrix
    • Gateway
    • Config
    • Bus
    • Stream
    • Sleuth
    • Nacos
    • Sentinel
      • 概述
        • hystrix的不足
        • Sentinel
      • 安装Sentinel控制台
        • 安装步骤
      • 初始化演示工程
        • cloudalibaba-sentinel-service8401
        • pom
        • yaml
        • 主启动类
        • 业务类FlowLimitController
      • 流控规则
        • 基本介绍
        • 进一步解释说明
        • 流控模式
        • 直接(默认)
        • QPS
        • 线程数
        • 关联
        • 链路
        • 流控效果
        • 直接->快速失败(默认效果)
        • 预热(Warm Up)
        • 应用场景
        • 排队等待
      • 降级规则
        • 慢调用比例
        • 异常比例
        • 异常数
      • 热点key限流
        • 测试接口
        • 添加热点规则配置
        • 测试结果
        • 参数例外项
        • 异常情况
      • 系统规则
        • 概念
      • @SentinelResource
        • 按资源名称限流+后续处理
        • 额外问题
        • 按url地址限流+后续处理
        • 上面兜底方案面临的问题
        • 自定义限流处理逻辑
        • 创建CustomerBlockHandler
        • 配置@SentinelResource参数
        • 测试
        • 更多注解属性说明
      • 服务熔断功能
        • 整合loadbalancer+fallback
        • provider(9003为例)
        • pom
        • yaml
        • 主启动类
        • 业务类
        • consumer84
        • pom
        • yaml
        • 业务类
        • 不配置fallback和blockHandler
        • 只配置fallback
        • 只配置blockHandler
        • fallback+blockHandler都配置
        • 忽略属性
        • 整合openfeign(84模块)
        • yaml
        • 主启动类
        • 业务类
        • 测试
      • 对比
      • 规则持久化
        • 修改8401模块
        • pom
        • yaml
        • 新增配置
        • 测试
    • Seata
  • 框架
  • SpringCloud
zdk
2022-11-18
目录

Sentinel

Table of Contents generated with DocToc (opens new window)

  • Sentinel
    • 概述
      • hystrix的不足
      • Sentinel
    • 安装Sentinel控制台
      • 安装步骤
    • 初始化演示工程
      • cloudalibaba-sentinel-service8401
        • pom
        • yaml
        • 主启动类
        • 业务类FlowLimitController
    • 流控规则
      • 基本介绍
        • 进一步解释说明
      • 流控模式
        • 直接(默认)
          • QPS
          • 线程数
        • 关联
        • 链路
      • 流控效果
        • 直接->快速失败(默认效果)
        • 预热(Warm Up)
          • 应用场景
        • 排队等待
    • 降级规则
      • 慢调用比例
      • 异常比例
      • 异常数
    • 热点key限流
      • 测试接口
      • 添加热点规则配置
      • 测试结果
      • 参数例外项
      • 异常情况
    • 系统规则
      • 概念
    • @SentinelResource
      • 按资源名称限流+后续处理
        • 额外问题
      • 按url地址限流+后续处理
      • 上面兜底方案面临的问题
      • 自定义限流处理逻辑
        • 创建CustomerBlockHandler
        • 配置@SentinelResource参数
        • 测试
      • 更多注解属性说明
    • 服务熔断功能
      • 整合loadbalancer+fallback
        • provider(9003为例)
          • pom
          • yaml
          • 主启动类
          • 业务类
        • consumer84
          • pom
          • yaml
          • 业务类
      • 不配置fallback和blockHandler
      • 只配置fallback
      • 只配置blockHandler
      • fallback+blockHandler都配置
      • 忽略属性
      • 整合openfeign(84模块)
        • yaml
        • 主启动类
        • 业务类
        • 测试
    • 对比
    • 规则持久化
      • 修改8401模块
        • pom
        • yaml
      • 新增配置
      • 测试

# Sentinel

# 概述

  • GitHub:https://github.com/alibaba/Sentinel
  • 官网:https://sentinelguard.io/zh-cn/

# hystrix的不足

  1. 需要手动搭建监控平台
  2. 没有一套web界面可以给我们进行更加细粒度化地对流控、速率、服务熔断、服务降级等进行控制

# Sentinel

其实就是前面的Hystrix的超集,但更加方便易用

image-20221119145733331

# 安装Sentinel控制台

直接在GitHub下载release版本的sentinel-dashboard.jar包即可,这里使用的是1.8.6版本

sentinel组件也是由两部分组成:即后台的监控程序+前台的dashboard

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

# 安装步骤

  • 下载好jar包以后,java -jar命令运行即可
  • 通过 http://localhost:8080访问管理界面,账号密码均为sentinel

image-20221119150445084

image-20221119150543494

# 初始化演示工程

  • 首先启动8848Nacos注册中心

  • 然后,新建cloudalibaba-sentinel-service8401模块

# cloudalibaba-sentinel-service8401

# pom

这里提前引入了一些后面需要的依赖,仅此处的话只需要nacos-discovery和spring-cloud-starter-alibaba-sentinel

<dependencies>
        <!--        通用的依赖-->
        <dependency>
            <groupId>com.zdk</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.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>
        <!-- nacos依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- ribbon依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!-- sentinel依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- 后续做持久化需要用到 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!-- openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# yaml

server:
  port: 8401


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

# actuator的必要配置
management:
  endpoints:
    web:
      exposure:
        include: '*'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 主启动类

@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401{
    public static void main(String[] args){
        SpringApplication.run(MainApp8401.class,args);
    }
}
1
2
3
4
5
6
7

# 业务类FlowLimitController

@RestController
public class FlowLimitController {

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

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 然后启动Sentinel8080、启动新建微服务8401
  • 启动后查看sentinel控制台

查看我们会发现并没有任何变化,这是因为sentinel是懒加载,我们只需要访问一次接口:

http://localhost:8401/testA 即可看到变化

image-20221119153333876

多访问几次接口就会出现实时监控的图表信息

image-20221119153556122

这就证实了sentinel正在监控8401微服务

# 流控规则

# 基本介绍

image-20221119154045404

# 进一步解释说明

  • 资源名:唯一名称,默认为请求的路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单机阈值:
    • QPS(每秒钟的请求数量):当调用该api的QPS达到设置的阈值时,进行限流
    • 线程数:当调用该api的线程数达到设定的阈值时,进行限流
  • 是否集群:sentinel是否是集群
  • 流控模式:
    • 直接:api达到限流条件时,直接限流
    • 关联:当关联的资源达到阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)[api级别的针对来源]
  • 流控效果:
    • 快速失败:直接失败,抛出异常
    • Warm Up:根据coldFactor(冷加载因子,默认3)的值,从阈值/coldFactor,经过预热时长,才达到设置的QPS阈值

# 流控模式

一般我们在簇点链路这里添加流控规则,因为资源名有了,不用再写,当然也可以在流控规则菜单添加

image-20221119155139349

# 直接(默认)

按照上图新增一个模式为直接的流控规则,QPS为1,这时对testA接口进行测试,1秒内多次点击,会出现Blocked by Sentinel (flow limiting)的提示,因为我们设置的每秒只能请求1次,若超过次数,就直接-快速失败,报默认错误

image-20221119155335443

# QPS

设置阈值类型为QPS时,是限制api每秒能接受的请求的个数,相当于多余的请求还未进入到应用程序就被限制

# 线程数

设置线程数,是限制这个api,同时只有设置的个数的线程在处理请求,假设我们设定线程数阈值为1,这时如果一个请求还未处理完,另一个请求又进来了,这个新进的请求就会出现Blocked by Sentinel (flow limiting)

# 关联

即当关联的资源达到阈值的时候,就限流自己,比如接口A关联了接口B,此时接口B达到阈值了,A就该对自己限流了

image-20221119161646241

我们用postman模拟对testB接口的并发密集访问,20个线程每0.3秒访问一次testB接口

image-20221119162438436

执行这个集合后再去访问testA,即限流成功,而等20个线程0.3秒共6秒访问testB完毕后,testA调用恢复正常

image-20221119162626835

# 链路

多个请求调用同一个微服务

# 流控效果

# 直接->快速失败(默认效果)

刚才已经测试了,即:直接失败,抛出异常Blocked by Sentinel (flow limiting)

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

# 预热(Warm Up)

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

image-20221119163746226

根据设置的这个图通俗解释一下:

在sentinel中,默认的冷加载因子coldFactor是3,所以在刚开始的时候,testB接口的QPS的阈值为 threshold/coldFactor = 10/3 = 3,然后预热时长为5(单位是秒),表示从接口第一次被访问开始的5秒内,testB的QPS阈值都是3,要直到5秒后,testB接口的QPS阈值才会从3慢慢上升到我们设置的10

# 应用场景

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

或者对应到缓存,先放一部分让缓存成功出现,再放大部分进来就都能走缓存而不是打到数据库了

# 排队等待

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

image-20221119165132840

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

image-20221119174805027

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

# 降级规则

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

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

# 慢调用比例

image-20221121152858138

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

执行的条件:

统计时长单位内请求数>=最小请求数 & 这些请求的平均响应时间大于统计时长 & 响应时间大于RT的请求数占总请求数的比例>设定的比例阈值

# 异常比例

image-20221121152916449

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

执行的条件:

统计时长单位内请求数>=设定的最小请求数 & 出现异常的请求占比>设定的异常比例阈值

# 异常数

image-20221121152933547

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

注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效

# 热点key限流

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

# 测试接口

@GetMapping(value = "/testHotKey")
//blockHandler用来指定不满足规则时的处理方法
//这个处理方法的参数类型和数量要和原接口一致,并新增一个BlockException类型的参数

//testHotKey为热点资源名称,配置时与代码中要一致
@SentinelResource(value = "testHotKey",blockHandler = "handleHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2){
    return "------testHotKey";
}
public String handleHotKey(String p1, String p2, BlockException exception){
    return "------handleHotKey---------";
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 添加热点规则配置

image-20221121163154702

# 测试结果

这里我们带着p1这个参数(这个参数在接口方法中的索引是0,即第一个参数)去访问接口,一秒内多次访问,即会走到blockHandler设定的方法,触发热点key限流

image-20221121163440288

  • 如果访问http://localhost:8401/testHotKey?p2=aaa,则不会触发限流,因为p2的参数索引是1而不是我们设置的0
  • 如果访问http://localhost:8401/testHotKey?p1=aaa&p2=aaa,也会触发限流

# 参数例外项

image-20221121164128744

上述的示例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流,但是这种情况不灵活,因为总有例外的情况,我们可能会希望这个参数的值为xx的时候,不被限流,或者被限流的阈值更大

  • 特例情况:
    • 普通:超过1秒一个后,达到阈值1马上被限流
    • 我们期望p1参数当它是某个特殊值时,它的限流规则和平时不一样
    • 特例:假如当p1的值为5时,它的阈值可以达到200

image-20221121164643755

测试多次访问http://localhost:8401/testHotKey?p1=1、http://localhost:8401/testHotKey?p1=5可以发现,p1为5时,并没有被限流了

# 异常情况

我们在接口中增加一句代码int a = 10/0;,再尝试去调用接口会发现,并没有走blockHandler指定的方法,也没有返回默认的异常,而是直接出现了Spring Boot默认的500页面

这是因为@SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理,而我们增加的int a = 10/0;是Java运行时异常,@SentinelResource并不会进行处理

# 系统规则

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

# 概念

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 和线程数四个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

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

系统规则支持以下的阈值类型:

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

这里以QPS=1测试一下,访问正常的testA和testB接口,同时访问就会有一个出现Blocked by Sentinel (flow limiting)

# @SentinelResource

# 按资源名称限流+后续处理

8401模块新增RateLimitController

@RestController
public class RateLimitController {

    @GetMapping(value = "/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource(){
        return new CommonResult(200, "按资源名称限流测试OK",new Payment(2022L, "serial0001"));
    }

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

按资源名设定限流规则:

image-20221121173644068

多次点击,出现限流,返回我们自定义的handler

image-20221121173750926

# 额外问题

关闭服务8401,发现流控规则消失了

# 按url地址限流+后续处理

新增controller接口

@GetMapping(value = "/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl(){
    return new CommonResult(200, "按url限流测试OK",new Payment(2022L, "serial0002"));
}
1
2
3
4
5

这里没有写blockHandler配置,限流返回的Sentinel默认的提示

# 上面兜底方案面临的问题

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

# 自定义限流处理逻辑

# 创建CustomerBlockHandler

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

    public static CommonResult handleException2(BlockException exception){
        return new CommonResult(444, "按客户自定义,global handleException----2");
    }
}
1
2
3
4
5
6
7
8
9

# 配置@SentinelResource参数

 @GetMapping(value = "/rateLimit/customerBlockHandler")
 @SentinelResource(value = "customerBlockHandler",
                   	 // 指定处理的类
                     blockHandlerClass = CustomerBlockHandler.class,
                     // 指定类中的哪个方法来处理
                     blockHandler = "handleException2")
 public CommonResult customerBlockHandler(){
     return new CommonResult(200, "按自定义限流测试OK",new Payment(2022L, "serial0003"));
 }
1
2
3
4
5
6
7
8
9

# 测试

这里进行配置的时候,必须对@SentinelResource的value作为资源名来配置限流规则,才能生效,而配置url为资源名则不会走自定义的兜底

image-20221121190334027

# 更多注解属性说明

  • SphU定义资源

  • Tracer定义统计

  • ContextUtil定义上下文

# 服务熔断功能

# 整合loadbalancer+fallback

image-20221121193426472

所以需要新建3个模块

  • cloudalibaba-provider-payment9003/9003
  • cloudalibaba-consumer-nacos-order84

# provider(9003为例)

# pom
<dependencies>
        <!--        通用的依赖-->
        <dependency>
            <groupId>com.zdk</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.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>
        <!-- nacos依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--  openfeign依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--  loadbalancer依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# yaml
server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
      nacos:
        discovery:
          server-addr: localhost:8848


management:
  endpoints:
    web:
      exposure:
        include: '*'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9003{
    public static void main(String[] args){
        SpringApplication.run(PaymentMain9003.class,args);
    }
}
1
2
3
4
5
6
7
# 业务类
@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, "256asdsadasd51a65s1d65ad165"));
        hashMap.put(2L,new Payment(2L, "j1211434shdas1a65s1d65ad165"));
        hashMap.put(3L,new Payment(3L, "256asdsadasddf11541asdada65"));
    }

    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        Payment payment = hashMap.get(id);
        return new CommonResult<>(200,"from mysql,serverPort:  "+serverPort,payment);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# consumer84

cloudalibaba-consumer-nacos-order84

# pom
<dependencies>
        <!--        通用的依赖-->
        <dependency>
            <groupId>com.zdk</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.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>
        <!-- nacos依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--  openfeign依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--  loadbalancer依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!-- sentinel依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- 后续做持久化需要用到 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# yaml
server:
  port: 84

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080

#消费者将要去访问的服务名
service-url:
  nacos-user-service: http://nacos-payment-provider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 业务类
@Configuration
public class ApplicationContextConfig {
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}
1
2
3
4
5
6
7
8
9
10

# 不配置fallback和blockHandler

遇到Java的异常,会直接返回默认的500页面

# 只配置fallback

	@GetMapping(value = "/consumer/fallback/{id}")	
    @SentinelResource(value = "fallback",fallback = "handlerFallback")//fallback只负责业务异常
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult res = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if (id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常.....");
        }else if (res.getData() == null){
            throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常.....");
        }
        return res;
    }

    public CommonResult<Payment> handlerFallback(@PathVariable("id") Long id,Throwable e){
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(444, "兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

image-20221122115335746

走我们的兜底方法

# 只配置blockHandler

	@GetMapping(value = "/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",blockHandler = "blockHandler")//blockHandler只负责sentinel控制台配置违规
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult res = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if (id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常.....");
        }else if (res.getData() == null){
            throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常.....");
        }
        return res;
    }

    public CommonResult<Payment> blockHandler(@PathVariable("id") Long id, BlockException blockException){
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(444, "blockHandler-sentinel限流,无此流水:blockException  "+blockException.getMessage(),payment);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

添加熔断规则

image-20221122115929074

1秒内2次请求2个异常

image-20221122120008551

# fallback+blockHandler都配置

@GetMapping(value = "/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("id") Long id){
        CommonResult res = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class);
        if (id == 4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常.....");
        }else if (res.getData() == null){
            throw new NullPointerException("NullPointerException,该id没有对应记录,空指针异常.....");
        }
        return res;
    }

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

    public CommonResult<Payment> blockHandler(@PathVariable("id") Long id, BlockException blockException){
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水:blockException  "+blockException.getMessage(),payment);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

这里简单来说就区分,

  • 如果sentinel的规则在业务之前执行(比如QPS),而出现不满足规则,此时不会走fallback
  • 如果sentinel规则是慢调用、异常比例、异常数等,是在业务执行时后的,在还不满足规则时候,走fallback,满足规则后,走blockHandler

# 忽略属性

@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
    exceptionsToIgnore = {IllegalArgumentException.class})
1
2

如果增加了这个配置,那么在方法中产生这个指定的异常时,就不会走fallback方法了

# 整合openfeign(84模块)

  • 首先引入openfeign的依赖

# yaml

增加sentinel对feign的支持配置

feign:
  sentinel:
    enabled: true
1
2
3

# 主启动类

主启动类上增加@EnableFeignClients注解

# 业务类

@FeignClient(value = "${service-url.nacos-user-service}",fallback = PaymentFallbackService.class)
public interface PaymentService {
    @GetMapping(value = "/paymentSQL/{id}")
    CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);

}

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

//CircleBreakerController新增以下接口进行测试
//    openfeign
    @Resource
    private PaymentService paymentService;

    @GetMapping(value = "/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        return paymentService.paymentSQL(id);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 测试

将payment9003/9004都关闭,进行测试,发现走了降级

image-20221122132102500

# 对比

Sentinel Hystrix resilience4j
隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号量隔离 信号量隔离
熔断降级策略 基于慢请求、异常比率、异常数 基于异常比率 基于响应时间、异常比率
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava) Ring Bit Buffer
动态规则配置 支持多种数据源 支持多种数据源 有限支持
扩展性 多个扩展点 插件的形式 接口的形式
基于注解的支持 支持 支持 支持
限流 基于QPS,支持基于调用关系的限流 有限的支持 Rate Limiter
流量整形 支持预热模式、匀速器模式、预热排队模式 不支持 简单的Rate Limiter模式
系统自适应保护 支持 不支持 不支持
控制台 提供开箱即用的控制台,可配置规则、查看秒级监控、机器发现等 简单的监控查看 不提供控制台,可对接其他监控系统

# 规则持久化

这里需要对规则进行持久化的原因是:在未持久化时,我们关闭受sentinel监控的应用之后,配置的相关规则就会消失

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

# 修改8401模块

# pom

新增依赖

<!-- 后续做持久化需要用到 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
1
2
3
4
5

# yaml

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #nacos 注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        # 配置Sentinel dashboard地址
        dashboard: localhost:8080
        # sentinel的端口 默认是8719 如果被占用会自动从8719开始依次+1扫描
        # 直到找到未被占用的端口为止
        port: 8719
      # sentinel持久化配置
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            data-id: ${spring.application.name}
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 新增配置

在nacos配置中心新增以下示例配置,与我们上面yaml中配置的data-id、group、data-type一致

image-20221122150319095

# 测试

  • 启动8401工程,访问http://localhost:8401/rateLimit/byUrl,再查看sentinel控制台,发现规则存在
  • 又停止8401工程,再查看规则,发现规则消失
  • 再启动8401工程,查看规则,规则没有,访问一两次接口后,规则出现
在 GitHub 上编辑此页 (opens new window)
#Sentinel#SpringCloud Alibaba
最后更新: 2022/11/22, 15:11:00
Nacos
Seata

← Nacos Seata→

Theme by Vdoing | Copyright © 2022-2025 zdk | notes
湘ICP备2022001117号-1
川公网安备 51142102511562号
本网站由 提供CDN加速/云存储服务
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式