005-CircuitBreaker断路器-Resilience4J

news2024/9/20 22:44:03

文章目录

  • 1 CircuitBreaker
    • 1.1 实现原理
    • 1.2 一句话
  • 2 Resilience4J
    • 2.1 是什么
    • 2.2 能干嘛
    • 2.3 怎么用
  • 3 熔断(CircuitBreaker)(服务熔断+服务降级)
    • 3.1 断路器三大状态
    • 3.2断路器3大状态之前的转换
    • 3.3断路器所有配置参数参考
    • 3.4 熔断+降级案例需求说明
    • 3.5 COUNT_BASED(计数的滑动窗口)
      • 3.5.1 cloud-provider-payment8001
      • 3.5.2 修改PayFeignApi接口
      • 3.5.3 修改cloud-consumer-feign-order80
        • 3.5.3.1 改POM
        • 3.5.3.2 写YML
        • 3.5.3.3 新建OrderCircuitController
        • 3.5.3.4 测试(按照错误次数达到多少后开启断路)
    • 3.6 TIME_BASED(时间的滑动窗口)
      • 3.6.1 cloud-consumer-feign-order80
      • 3.6.2 为避免影响实验效果,记得关闭FeignConfig自己写的重试3次
      • 3.6.3 测试(慢查询)
        • 3.6.3.1 一次超时,一次正常访问,同时进行
        • 3.6.3.2 4次超时访问,一次正常访问,同时进行
    • 3.7 小总结
    • 断路器开启或者关闭的条件
  • 4 隔离(BulkHead)
    • 4.1 是什么
    • 4.2 能干嘛
    • 4.3 Resilience4j提供了如下两种隔离的实现方式,可以限制并发执行的数量
    • 4.4 实现SemaphoreBulkhead(信号量舱壁)
      • 4.4.1 概述
      • 4.4.2 源码分析
      • 4.4.3 cloud-provider-payment8001支付微服务修改PayCircuitController
      • 4.4.4 PayFeignApi接口新增舱壁api方法
      • 4.4.5 修改cloud-consumer-feign-order80
        • 4.4.5.1 POM
        • 4.4.5.2 YML
        • 4.4.5.3 业务类
        • 4.4.5.4 测试
    • 4.5 实现FixedThreadPoolBulkhead(固定线程池舱壁)
      • 4.5.1 概述
      • 4.5.2 源码分析
      • 4.5.3 修改cloud-consumer-feign-order80
        • 4.5.3.1 POM
        • 4.5.3.2 YML
        • 4.5.3.3 controller
  • 5 限流(RateLimiter)
    • 5.1 是什么
    • 5.2 面试题:说说常见限流算法
    • 5.2.1 漏斗算法(Leaky Bucket)
    • 5.2.2 令牌桶算法(Token Bucket)
    • 5.2.3 滚动时间窗(tumbling time window)
    • 5.2.4 滑动时间窗口(sliding time window)
    • 5.3 cloud-provider-payment8001支付微服务
    • 5.4 PayFeignApi接口新增限流api方法
    • 5.5 修改cloud-consumer-feign-order80
      • 5.5.1 POM
      • 5.5.2
      • 5.5.3 controller
    • 5.6 测试

通过百度网盘分享的文件:springcloud
链接:https://pan.baidu.com/s/1lTChHsKgJpvvFRnq7WQImQ
提取码:msr3

1 CircuitBreaker

官网:https://spring.io/projects/spring-cloud-circuitbreaker#overview

1.1 实现原理

CircuitBreaker的目的是保护分布式系统免受故障和异常,提高系统的可用性和健壮性。

当一个组件或服务出现故障时,CircuitBreaker会迅速切换到开放OPEN状态(保险丝跳闸断电),阻止请求发送到该组件或服务从而避免更多的请求发送到该组件或服务。这可以减少对该组件或服务的负载,防止该组件或服务进一步崩溃,并使整个系统能够继续正常运行。同时,CircuitBreaker还可以提高系统的可用性和健壮性,因为它可以在分布式系统的各个组件之间自动切换,从而避免单点故障的问题。
在这里插入图片描述

1.2 一句话

Circuit Breaker只是一套规范和接口,我们使用的落地实现者是Resilience4J

2 Resilience4J

2.1 是什么

官网:https://github.com/resilience4j/resilience4j#1-introduction

在这里插入图片描述

2.2 能干嘛

https://github.com/resilience4j/resilience4j#3-overview
在这里插入图片描述

2.3 怎么用

官网:https://resilience4j.readme.io/docs/circuitbreaker
中文手册:https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/index.md

3 熔断(CircuitBreaker)(服务熔断+服务降级)

3.1 断路器三大状态

在这里插入图片描述

3.2断路器3大状态之前的转换

在这里插入图片描述

3.3断路器所有配置参数参考

英文:https://resilience4j.readme.io/docs/circuitbreaker#create-and-configure-a-circuitbreaker
中文手册:https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/core-modules/CircuitBreaker.md
精简:
在这里插入图片描述

3.4 熔断+降级案例需求说明

6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。
等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。
如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求。

具体时间和频次等属性见具体实际案例,这里只是作为case举例讲解,最下面笔记面试题概览,闲聊大厂面试
在这里插入图片描述

3.5 COUNT_BASED(计数的滑动窗口)

3.5.1 cloud-provider-payment8001

新建PayCircuitController

@RestController
public class PayCircuitController
{
    //=========Resilience4j CircuitBreaker 的例子
    @GetMapping(value = "/pay/circuit/{id}")
    public String myCircuit(@PathVariable("id") Integer id)
    {
        if(id == -4) throw new RuntimeException("----circuit id 不能负数");
        if(id == 9999){
            try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
        }
        return "Hello, circuit! inputId:  "+id+" \t " + IdUtil.simpleUUID();
    }
}

3.5.2 修改PayFeignApi接口

添加方法

 /**
     * Resilience4j CircuitBreaker 的例子
     * @param id
     * @return
     */
    @GetMapping(value = "/pay/circuit/{id}")
    public String myCircuit(@PathVariable("id") Integer id);

3.5.3 修改cloud-consumer-feign-order80

3.5.3.1 改POM
<!--resilience4j-circuitbreaker-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!-- 由于断路保护等需要AOP实现,所以必须导入AOP包 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3.5.3.2 写YML
server:
  port: 80

spring:
  application:
    name: cloud-consumer-openfeign-order
  ####Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true #优先使用服务ip进行注册
        service-name: ${spring.application.name}
    ####### openfeign
    openfeign:
      client:
        config:
    ##### 超时控制
#          default: #全局配置
#            #连接超时时间
#            connect-timeout: 3000
#            #读取超时时间
#            read-timeout: 3000
          cloud-payment-service:
            #连接超时时间
            connectTimeout: 20000
            #读取超时时间
            readTimeout: 20000
      httpclient:
        hc5:
          enabled: true
      compression:
        request:
          enabled: true
          min-request-size: 2048 #最小触发压缩的大小
          mime-types: text/xml,application/xml,application/json #触发压缩数据类型
        response:
          enabled: true
# 开启circuitbreaker和分组激活 spring.cloud.openfeign.circuitbreaker.enabled
      circuitbreaker:
        enabled: true
#        group:
#          enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后

# feign日志以什么级别监控哪个接口
#logging:
#  level:
#    com:
#      msr:
#        cloud:
#          apis:
#            PayFeignApi: debug # PayFeignApi接口需要打印日志


# Resilience4j CircuitBreaker 按照次数:COUNT_BASED 的例子
#  6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。
#  等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。
#  如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求。
resilience4j:
  circuitbreaker:
    configs:
      default:
        failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。
        slidingWindowType: COUNT_BASED # 滑动窗口的类型
        slidingWindowSize: 6 #滑动窗⼝的⼤⼩配置COUNT_BASED表示6个请求,配置TIME_BASED表示6秒
        minimumNumberOfCalls: 6 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。如果minimumNumberOfCalls为10,则必须最少记录10个样本,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。
        automaticTransitionFromOpenToHalfOpenEnabled: true # 是否启用自动从开启状态过渡到半开状态,默认值为true。如果启用,CircuitBreaker将自动从开启状态过渡到半开状态,并允许一些请求通过以测试服务是否恢复正常
        waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间
        permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。在半开状态下,CircuitBreaker将允许最多permittedNumberOfCallsInHalfOpenState个请求通过,如果其中有任何一个请求失败,CircuitBreaker将重新进入开启状态。
        recordExceptions:
          - java.lang.Exception
    instances:
      cloud-payment-service:
        baseConfig: default
3.5.3.3 新建OrderCircuitController

@CircuitBreaker
系统繁忙,请稍后再试。
不让调用者等待并立刻返回一个友好提示,fallback

@RestController
public class OrderCircuitController
{
    @Resource
    private PayFeignApi payFeignApi;

    @GetMapping(value = "/feign/pay/circuit/{id}")
    @CircuitBreaker(name = "cloud-payment-service", fallbackMethod = "myCircuitFallback")
    public String myCircuitBreaker(@PathVariable("id") Integer id)
    {
        return payFeignApi.myCircuit(id);
    }
    //myCircuitFallback就是服务降级后的兜底处理方法
        public String myCircuitFallback(Integer id,Throwable t) {
        // 这里是容错处理逻辑,返回备用结果
        return "myCircuitFallback,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
    }
}
3.5.3.4 测试(按照错误次数达到多少后开启断路)

正确:http://localhost/feign/pay/circuit/11
错误:http://localhost/feign/pay/circuit/-4

一次error一次OK,trytry看看
50%错误后触发熔断并给出服务降级,告知调用者服务不可用
此时就算是输入正确的访问地址也无法调用服务(我明明是正确的也不让用/(ㄒoㄒ)/~~),它还在断路中(OPEN状态),一会儿过度到半开并继续正确地址访问,慢慢切换回CLOSE状态,可以正常访问了链路回复

多次故意填写错误值(负4),多次故意填写错误值(负4),然后慢慢填写正确值(正整数11),发现刚开始不满足条件,就算是正确的访问地址也不能进行

3.6 TIME_BASED(时间的滑动窗口)

在这里插入图片描述

3.6.1 cloud-consumer-feign-order80

server:
  port: 80

spring:
  application:
    name: cloud-consumer-openfeign-order
  ####Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true #优先使用服务ip进行注册
        service-name: ${spring.application.name}
    openfeign:
      client:
        config:
          default:
            #cloud-payment-service:
            #连接超时时间,为避免演示出错,讲解完本次内容后设置为20秒
            connectTimeout: 20000
            #读取超时时间,为避免演示出错,讲解完本次内容后设置为20秒
            readTimeout: 20000
            #开启httpclient5
      httpclient:
        hc5:
          enabled: true
          #开启压缩特性
      compression:
        request:
          enabled: true
          min-request-size: 2048
          mime-types: text/xml,application/xml,application/json
        response:
          enabled: true
      #开启circuitbreaker和分组激活
      circuitbreaker:
        enabled: true
        group:
          enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后


# feign日志以什么级别监控哪个接口
logging:
  level:
    com:
      atguigu:
        cloud:
          apis:
            PayFeignApi: debug

# Resilience4j CircuitBreaker 按照时间:TIME_BASED 的例子
resilience4j:
  timelimiter:
    configs:
      default:
        timeout-duration: 10s #神坑的位置,timelimiter 默认限制远程1s,超于1s就超时异常,配置了降级,就走降级逻辑
  circuitbreaker:
    configs:
      default:
        failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。
        slowCallDurationThreshold: 2s #慢调用时间阈值,高于这个阈值的视为慢调用并增加慢调用比例。
        slowCallRateThreshold: 30 #慢调用百分比峰值,断路器把调用时间⼤于slowCallDurationThreshold,视为慢调用,当慢调用比例高于阈值,断路器打开,并开启服务降级
        slidingWindowType: TIME_BASED # 滑动窗口的类型
        slidingWindowSize: 2 #滑动窗口的大小配置,配置TIME_BASED表示2秒
        minimumNumberOfCalls: 2 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。
        permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。
        waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间
        recordExceptions:
          - java.lang.Exception
    instances:
      cloud-payment-service:
        baseConfig: default 

3.6.2 为避免影响实验效果,记得关闭FeignConfig自己写的重试3次

在这里插入图片描述

3.6.3 测试(慢查询)

3.6.3.1 一次超时,一次正常访问,同时进行

http://localhost/feign/pay/circuit/9999
故意超时,将会单独报错

http://localhost/feign/pay/circuit/11
可以访问,我是正常的

3.6.3.2 4次超时访问,一次正常访问,同时进行

超时进行:http://localhost/feign/pay/circuit/9999
正常访问也受到了牵连,因为服务熔断不能访问了:http://localhost/feign/pay/circuit/11
运气好的话,可以看到全线崩,刺激。

3.7 小总结

断路器开启或者关闭的条件

当满足一定的峰值和失败率达到一定条件后,断路器将会进入OPEN状态(保险丝跳闸),服务熔断
当OPEN的时候,所有请求都不会调用主业务逻辑方法,而是直接走fallbackmetnod兜底背锅方法,服务降级
一段时间之后,这个时候断路器会从OPEN进入到HALF_OPEN半开状态,会放几个请求过去探探链路是否通?
如成功,断路器会关闭CLOSE(类似保险丝闭合,恢复可用);
如失败,继续开启。重复上述

建议不要混合用,推荐按照调用次数count_based
在这里插入图片描述

4 隔离(BulkHead)

官网:https://resilience4j.readme.io/docs/bulkhead
中文:https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/core-modules/bulkhead.md

4.1 是什么

bulkhead(船的)舱壁/(飞机的)隔板

隔板来自造船行业,床仓内部一般会分成很多小隔舱,一旦一个隔舱漏水因为隔板的存在而不至于影响其它隔舱和整体船
在这里插入图片描述

4.2 能干嘛

依赖隔离&负载保护:用来限制对于下游服务的最大并发数量的限制
在这里插入图片描述

4.3 Resilience4j提供了如下两种隔离的实现方式,可以限制并发执行的数量

在这里插入图片描述

4.4 实现SemaphoreBulkhead(信号量舱壁)

4.4.1 概述

基本上就是我们JUC信号灯内容的同样思想
在这里插入图片描述
信号量舱壁(SemaphoreBulkhead)原理

当信号量有空闲时,进入系统的请求会直接获取信号量并开始业务处理。

当信号量全被占用时,接下来的请求将会进入阻塞状态,SemaphoreBulkhead提供了一个阻塞计时器,

如果阻塞状态的请求在阻塞计时内无法获取到信号量则系统会拒绝这些请求。

若请求在阻塞计时内获取到了信号量,那将直接获取信号量并执行相应的业务处理。

4.4.2 源码分析

io.github.resilience4j.bulkhead.internal.SemaphoreBulkhead
在这里插入图片描述

4.4.3 cloud-provider-payment8001支付微服务修改PayCircuitController

//=========Resilience4j bulkhead 的例子
@GetMapping(value = "/pay/bulkhead/{id}")
public String myBulkhead(@PathVariable("id") Integer id)
{
    if(id == -4) throw new RuntimeException("----bulkhead id 不能-4");

    if(id == 9999)
    {
        try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
    }

    return "Hello, bulkhead! inputId:  "+id+" \t " + IdUtil.simpleUUID();
}

4.4.4 PayFeignApi接口新增舱壁api方法

/**
 * Resilience4j Bulkhead 的例子
 * @param id
 * @return
 */
@GetMapping(value = "/pay/bulkhead/{id}")
public String myBulkhead(@PathVariable("id") Integer id);

4.4.5 修改cloud-consumer-feign-order80

4.4.5.1 POM
<!--resilience4j-bulkhead-->
<dependency>
   <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-bulkhead</artifactId>
</dependency>
4.4.5.2 YML

在这里插入图片描述

server:
  port: 80

spring:
  application:
    name: cloud-consumer-openfeign-order
  ####Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true #优先使用服务ip进行注册
        service-name: ${spring.application.name}
    openfeign:
      client:
        config:
          default:
          #cloud-payment-service:
            #连接超时时间,为避免演示出错,讲解完本次内容后设置为20秒
            connectTimeout: 20000
            #读取超时时间,为避免演示出错,讲解完本次内容后设置为20秒
            readTimeout: 20000
            #开启httpclient5
      httpclient:
        hc5:
          enabled: true
          #开启压缩特性
      compression:
        request:
          enabled: true
          min-request-size: 2048
          mime-types: text/xml,application/xml,application/json
        response:
          enabled: true
      #开启circuitbreaker和分组激活
      circuitbreaker:
        enabled: true
        group:
          enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后


# feign日志以什么级别监控哪个接口
logging:
  level:
    com:
      atguigu:
        cloud:
          apis:
            PayFeignApi: debug

####resilience4j bulkhead 的例子
resilience4j:
  bulkhead:
    configs:
      default:
        maxConcurrentCalls: 2 # 隔离允许并发线程执行的最大数量
                maxWaitDuration: 1s # 当达到并发调用数量时,新的线程的阻塞时间,我只愿意等待1秒,过时不候进舱壁兜底fallback
    instances:
      cloud-payment-service:
        baseConfig: default
  timelimiter:
    configs:
      default:
        timeout-duration: 20s
4.4.5.3 业务类

OrderCircuitController

/**
 *(船的)舱壁,隔离
 * @param id
 * @return
 */
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadFallback",type = Bulkhead.Type.SEMAPHORE)
public String myBulkhead(@PathVariable("id") Integer id)
{
    return payFeignApi.myBulkhead(id);
}
public String myBulkheadFallback(Throwable t)
{
    return "myBulkheadFallback,隔板超出最大数量限制,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
}
4.4.5.4 测试

浏览器新打开2个窗口,各点一次,分别点击http://localhost/feign/pay/bulkhead/9999

每个请求调用需要耗时5秒,2个线程瞬间达到配置过的最大并发数2

此时第3个请求正常的请求访问,http://localhost/feign/pay/bulkhead/3

直接被舱壁限制隔离了,碰不到8001

等其中一个窗口停止了,再去正常访问,并发数小于2 了,可以OK
在这里插入图片描述

4.5 实现FixedThreadPoolBulkhead(固定线程池舱壁)

4.5.1 概述

基本上就是我们JUC-线程池内容的同样思想
在这里插入图片描述
固定线程池舱壁(FixedThreadPoolBulkhead)
FixedThreadPoolBulkhead的功能与SemaphoreBulkhead一样也是用于限制并发执行的次数的,但是二者的实现原理存在差别而且表现效果也存在细微的差别。FixedThreadPoolBulkhead使用一个固定线程池和一个等待队列来实现舱壁。
当线程池中存在空闲时,则此时进入系统的请求将直接进入线程池开启新线程或使用空闲线程来处理请求。
当线程池中无空闲时时,接下来的请求将进入等待队列,
若等待队列仍然无剩余空间时接下来的请求将直接被拒绝,
在队列中的请求等待线程池出现空闲时,将进入线程池进行业务处理。
另外:ThreadPoolBulkhead只对CompletableFuture方法有效,所以我们必创建返回CompletableFuture类型的方法

4.5.2 源码分析

io.github.resilience4j.bulkhead.internal.FixedThreadPoolBulkhead

底子就是JUC里面的线程池ThreadPoolExecutor
在这里插入图片描述
submit进线程池返回CompletableFuture
在这里插入图片描述

4.5.3 修改cloud-consumer-feign-order80

4.5.3.1 POM
<!--resilience4j-bulkhead-->
<dependency>
   <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-bulkhead</artifactId>
</dependency>
4.5.3.2 YML

在这里插入图片描述

server:
  port: 80

spring:
  application:
    name: cloud-consumer-openfeign-order
  ####Spring Cloud Consul for Service Discovery
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true #优先使用服务ip进行注册
        service-name: ${spring.application.name}
    openfeign:
      client:
        config:
          default:
            #cloud-payment-service:
            #连接超时时间,为避免演示出错,讲解完本次内容后设置为20秒
            connectTimeout: 20000
            #读取超时时间,为避免演示出错,讲解完本次内容后设置为20秒
            readTimeout: 20000
            #开启httpclient5
      httpclient:
        hc5:
          enabled: true
          #开启压缩特性
      compression:
        request:
          enabled: true
          min-request-size: 2048
          mime-types: text/xml,application/xml,application/json
        response:
          enabled: true
      #开启circuitbreaker和分组激活
      circuitbreaker:
        enabled: true
#        group:
#          enabled: true # 演示Bulkhead.Type.THREADPOOL时spring.cloud.openfeign.circuitbreaker.group.enabled

设为false新启线程和原来主线程脱离了。



# feign日志以什么级别监控哪个接口
logging:
  level:
    com:
      atguigu:
        cloud:
          apis:
            PayFeignApi: debug

####resilience4j bulkhead -THREADPOOL的例子
resilience4j:
  timelimiter:
    configs:
      default:
        timeout-duration: 10s #timelimiter默认限制远程1s,超过报错不好演示效果所以加上10秒
  thread-pool-bulkhead:
    configs:
      default:
        core-thread-pool-size: 1
        max-thread-pool-size: 1
        queue-capacity: 1
    instances:
      cloud-payment-service:
        baseConfig: default
# spring.cloud.openfeign.circuitbreaker.group.enabled 请设置为false 新启线程和原来主线程脱离

在这里插入图片描述

4.5.3.3 controller
/**
 * (船的)舱壁,隔离,THREADPOOL
 * @param id
 * @return
 */
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadPoolFallback",type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<String> myBulkheadTHREADPOOL(@PathVariable("id") Integer id)
{
    System.out.println(Thread.currentThread().getName()+"\t"+"enter the method!!!");
    try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
    System.out.println(Thread.currentThread().getName()+"\t"+"exist the method!!!");

    return CompletableFuture.supplyAsync(() -> payFeignApi.myBulkhead(id) + "\t" + " Bulkhead.Type.THREADPOOL");
}
public CompletableFuture<String> myBulkheadPoolFallback(Integer id,Throwable t)
{
    return CompletableFuture.supplyAsync(() -> "Bulkhead.Type.THREADPOOL,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~");
}

5 限流(RateLimiter)

官网:https://resilience4j.readme.io/docs/ratelimiter
中文:https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/core-modules/ratelimiter.md

5.1 是什么

限流 就是限制最大访问流量。系统能提供的最大并发是有限的,同时来的请求又太多,就需要限流。

比如商城秒杀业务,瞬时大量请求涌入,服务器忙不过就只好排队限流了,和去景点排队买票和去医院办理业务排队等号道理相同
在这里插入图片描述
所谓限流,就是通过对并发访问/请求进行限速,或者对一个时间窗口内的请

求进行限速,以保护应用系统,一旦达到限制速率则可以拒绝服务、排队或

等待、降级等处理。

5.2 面试题:说说常见限流算法

5.2.1 漏斗算法(Leaky Bucket)

一个固定容量的漏桶,按照设定常量固定速率流出水滴,类似医院打吊针,不管你源头流量多大,我设定匀速流出。

如果流入水滴超出了桶的容量,则流入的水滴将会溢出了(被丢弃),而漏桶容量是不变的。
在这里插入图片描述

5.2.2 令牌桶算法(Token Bucket)

在这里插入图片描述

5.2.3 滚动时间窗(tumbling time window)

允许固定数量的请求进入(比如1秒取4个数据相加,超过25值就over)超过数量就拒绝或者排队,等下一个时间段进入。

由于是在一个时间间隔内进行限制,如果用户在上个时间间隔结束前请求(但没有超过限制),同时在当前时间间隔刚开始请求(同样没超过限制),在各自的时间间隔内,这些请求都是正常的。下图统计了3次,but…
在这里插入图片描述
缺点:缺点:间隔临界的一段时间内的请求就会超过系统限制,可能导致系统被压垮
由于计数器算法存在时间临界点缺陷,因此在时间临界点左右的极短时间段内容易遭到攻击。
在这里插入图片描述
假如设定1分钟最多可以请求100次某个接口,如12:00:00-12:00:59时间段内没有数据请求但12:00:59-12:01:00时间段内突然并发100次请求,紧接着瞬间跨入下一个计数周期计数器清零;在12:01:00-12:01:01内又有100次请求。那么也就是说在时间临界点左右可能同时有2倍的峰值进行请求,从而造成后台处理请求加倍过载的bug,导致系统运营能力不足,甚至导致系统崩溃,/(ㄒoㄒ)/~~

double kill

5.2.4 滑动时间窗口(sliding time window)

滑动时间窗口(sliding time window)

顾名思义,该时间窗口是滑动的。所以,从概念上讲,这里有两个方面的概念需要理解:

  • 窗口:需要定义窗口的大小

  • 滑动:需要定义在窗口中滑动的大小,但理论上讲滑动的大小不能超过窗口大小

滑动窗口算法是把固定时间片进行划分并且随着时间移动,移动方式为开始时间点变为时间列表中的第2个时间点,结束时间点增加一个时间点,

不断重复,通过这种方式可以巧妙的避开计数器的临界点的问题。下图统计了5次
在这里插入图片描述

5.3 cloud-provider-payment8001支付微服务

修改PayCircuitController新增myRatelimit方法

//=========Resilience4j ratelimit 的例子
@GetMapping(value = "/pay/ratelimit/{id}")
public String myRatelimit(@PathVariable("id") Integer id)
{
    return "Hello, myRatelimit欢迎到来 inputId:  "+id+" \t " + IdUtil.simpleUUID();
}

5.4 PayFeignApi接口新增限流api方法

/**
 * Resilience4j Ratelimit 的例子
 * @param id
 * @return
 */
@GetMapping(value = "/pay/ratelimit/{id}")
public String myRatelimit(@PathVariable("id") Integer id);

5.5 修改cloud-consumer-feign-order80

5.5.1 POM

<!--resilience4j-ratelimiter-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-ratelimiter</artifactId>
</dependency>

5.5.2

####resilience4j ratelimiter 限流的例子
resilience4j:
  ratelimiter:
    configs:
      default:
        limitForPeriod: 2 #在一次刷新周期内,允许执行的最大请求数
        limitRefreshPeriod: 1s # 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriod
        timeout-duration: 1 # 线程等待权限的默认等待时间
    instances:
        cloud-payment-service:
          baseConfig: default

5.5.3 controller

@GetMapping(value = "/feign/pay/ratelimit/{id}")
@RateLimiter(name = "cloud-payment-service",fallbackMethod = "myRatelimitFallback")
public String myBulkhead(@PathVariable("id") Integer id)
{
    return payFeignApi.myRatelimit(id);
}
public String myRatelimitFallback(Integer id,Throwable t)
{
    return "你被限流了,禁止访问/(ㄒoㄒ)/~~";
}

5.6 测试

http://localhost/feign/pay/ratelimit/11
在这里插入图片描述刷新上述地址,正常后F5按钮狂刷一会儿,停止刷新看到被限流的效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2086751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6个一键生成原创文案实用方法,亲测好用!

在当下的这个自媒体时代&#xff0c;文案创作的需求日益增长。无论是用于社交媒体、广告宣传还是各种内容创作&#xff0c;优质的原创文案都能起到关键作用。但有时候&#xff0c;我们在创作文案的过程中可能会陷入灵感枯竭的困境。但别担心&#xff0c;这里有6个一键生成原创文…

iOS App快捷指令(App Intents)在系统搜索服务中注册shortcuts

iOS App快捷指令(App Intents)在系统搜索服务中注册shortcuts 前言效果图实现快捷指令1. 定义AppIntent2. 定义`AppShortcutsProvider使用参考资料前言 网上很多资料都是关于IntentsExtension的,但是IntentsExtension只能实现快捷指令的添加,无法在系统搜索服务中搜索到。…

嵌入式day36

数据库 专业存储数据、大量数据 数组、链表、变量---->内存&#xff1a;程序运行结束、掉电数据丢失 文件---->硬盘&#xff1a;程序运行结束、掉电数据不丢失 数据库---->硬盘 数据库文件与普通文件区别&#xff1a; 1.普通文件对数据管理&#xff08;增删改查…

提前还房贷结果失败了该怎么办?需要注意哪些?怎么做更顺利?

提前还房贷结果失败了&#xff0c;该怎么办&#xff1f; 1. 满足条件再申请&#xff1a;部分银行对提前还款设有一定的条件和限制&#xff0c;例如需要提前预约&#xff0c;对已还款时间和还款金额也有具体的要求。如果借款人未能满足这些条件&#xff0c;提前还款的申请可能会…

0829作业+思维导图

一、作业 代码&#xff1a; #include <iostream> #include <string.h> #include <stdio.h> using namespace std; class Mystring { public://无参构造Mystring():size(10){str new char[size];cout<<"无参构造完成"<<endl;}//有参…

【Java】—— Java面向对象进阶:Java银行账户管理系统设计与实现

目录 1. 账户类&#xff08;Account&#xff09; 2. 客户类&#xff08;Customer&#xff09; 3. 银行类&#xff08;Bank&#xff09; 4. 测试类&#xff08;BankTest&#xff09; 运行结果 在今天的博文中&#xff0c;我们将一起探讨一个简单的Java银行账户管理系统的设…

【JavaEE初阶】HTTP请求(Request)

&#x1f4d5;引言 HTTP 请求报文由请求行、请求头部、空行 和 请求包体 4 个部分组成 本片文章将从以下四个方面对HTTP请求报文进行解析 URL方法请求报头正文 &#x1f384;认识URL 我们先抓一个包来看一下URL在包里面的位置 平时我们俗称的 “网址” 其实就是说的 URL (…

数据库——开篇

一、数据存储方式 1、内存存数据&#xff1a;当程序运行结束&#xff0c;掉电&#xff0c;数据丢失。&#xff08;数组、链表、变量等&#xff09; 2、硬盘存数据&#xff1a;程序运行结束&#xff0c;掉电&#xff0c;数据不丢失。 &#xff08;1&#xff09;文件&#xff…

盘点2024年wind录屏好用工具,这四款打工人常备!

嘿&#xff0c;朋友们&#xff0c;今天我要给大家分享一下关于Windows自带录屏系统以及几款热门录屏软件的使用方法和体验感。录屏功能对于很多小伙伴来说可是个宝贝&#xff0c;无论你是要录制游戏过程、制作教学视频还是记录生活小确幸&#xff0c;都能派上用场。接下来&…

HAL库:串口 不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

目录 HAL库&#xff1a;不定长数据接收 循环收发缓冲区 空闲中断 收发数据 串口&#xff1a;多指针定位收发循环使用缓冲区设计 文件架构&#xff1a; 程序部分大体思路 Uart.h Uart.c stm32fxx_It.c main.c HAL库&#xff1a;不定长数据接收 循环收发缓冲区 空闲中…

SOCKS5代理配置中的常见问题:故障排除指南

SOCKS5代理是一种灵活的网络代理协议&#xff0c;被广泛用于提高网络隐私、访问受限内容、绕过网络限制等。然而&#xff0c;用户在配置SOCKS5代理时经常会遇到各种问题&#xff0c;这些问题可能会影响网络连接的稳定性和安全性。 了解这些常见问题及其解决方案可以帮助用户更…

水陆双雄:赛艇与VELO Angel Revo Halo坐垫的平衡共舞~

在广阔的水域上&#xff0c;赛艇运动员们以划桨为笔&#xff0c;书写着速度与激情的篇章。每一桨的挥动&#xff0c;都是力量与技巧的完美结合。而在自行车运动中&#xff0c;VELO Angel Revo Halo坐垫则如同骑行者的守护神&#xff0c;以科技与环保之名&#xff0c;为长途跋涉…

【开学季】你需要这样一个桌面倒计时 时刻提醒你不负韶华

【开学季】你需要这样一个桌面倒计时 时刻提醒你不负韶华。又到开学季了&#xff0c;莘莘学子陆续的回归到校园&#xff0c;开启人生价值的提升。 时光荏苒&#xff0c;岁月如梭&#xff0c;转眼间又是开学季。这个季节&#xff0c;总是承载着太多的期待与梦想&#xff0c;它像…

推荐2024年新手友好的4款音乐剪辑软件!

最近我开始接触音乐剪辑&#xff0c;想把一些歌曲进行剪辑创作&#xff1b;于是在网上好多了很多的音频剪辑软件进行试用&#xff0c;一番下来&#xff0c;发现了4款使用起来体验感比较好的专业剪辑工具&#xff0c;在这里跟大家分享分享。这些工具都可以被应用于歌曲创作&…

样式(3)----修改主题颜色

楔子&#xff1a;做软件时&#xff0c;经常有这样的需求&#xff0c;这样配色不合适&#xff1f;或者像动态的修改样式&#xff1f;那问题来了&#xff1a;怎样修软件界面的主题、修改皮肤&#xff1f; 方法1&#xff1a;使用第三方控件&#xff0c;直接更换主题&#xff0c;p…

电商库存API:商家数字化转型的加速器

在电商行业&#xff0c;库存管理是运营的核心之一。随着业务的扩展和消费者需求的多样化&#xff0c;传统的库存管理方法已经难以满足现代电商的需求。这时&#xff0c;电商库存API应运而生&#xff0c;为商家提供了一种高效、灵活的库存管理手段。 一、对接电商库存API的重要…

如何用SpringBoot搭建摇滚乐鉴赏网站?一步步教你实现音乐分享与社区互动!

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

高效产品设计:十大关键经验教程

在当今这个竞争异常激烈的市场环境中&#xff0c;产品系统设计的效率已经迅速成为企业能否取得成功的关键因素之一。随着消费者需求的不断变化和技术发展的日新月异&#xff0c;企业必须快速响应市场动态&#xff0c;以便在激烈的竞争中保持优势。而一套高效的产品系统设计方法…

js逆向——异步栈分析(上)

案例1&#xff1a; 受害者网站&#xff1a;https://m.ctyun.cn/wap/main/auth/login 首先抓包分析一波 发现是xhr请求&#xff0c;可以跟栈调试&#xff0c;相对还是容易一些 前面三个一般是send,ajax,xmlhttprequest这种封装好的发包的库&#xff0c;一般不用在意&#xff…

图像数据处理25

六、 图像分割 6.2区域生长 6.2.1区域生长的基本概念&#xff1a;可以理解成先选择一个像素区域&#xff0c;然后将其邻接区域中相似程度高的像素合并进去&#xff0c;直到找不到可以合并的像素为止。其的主要优点是能够较为精准地将具有相同特征的联通区域分割出来。 6.2.2…