0
点赞
收藏
分享

微信扫一扫

webGL 综合教程100+【目录】

龙驹书房 2024-09-18 阅读 29

服务雪崩:指在多个服务之间存在依赖关系时,当一个服务发生故障或不可用时,导致其他服务也无法正常工作的情况。这种现象通常是因为服务之间的依赖关系过于紧密,当一个服务发生故障时,其他服务无法正确处理该服务的请求或返回结果,从而导致级联故障,整个系统无法正常运行。所以,有问题的节点,快速熔断(快速返回失败处理或者返回默认兜底数据【服务降级】)。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

服务熔断:服务熔断(Circuit Breaker)是一种用来控制服务间调用的一种机制,目的是为了减少故障对系统的影响。当某个服务出现故障或者响应时间过长时,熔断器会自动打开,停止向该服务发送请求,而是返回一个预设的错误响应。这样可以防止故障或响应缓慢的服务拖垮整个系统,同时也可以快速失败,减少用户的等待时间。

服务降级:服务降级是指在系统负载过高或者出现异常情况下,为了保证核心功能的稳定性和可用性,暂时关闭一些非核心功能或者降低服务的质量,以保证系统整体的稳定运行。服务降级可以通过调整系统配置参数、限流、降低并发请求等方式实现。

服务限流 服务限时 服务预热 接近实时的监控 兜底的处理动作

Circuit Breaker:一种设计模式,用于在分布式系统中处理故障和延迟。在分布式系统中,服务之间通常会相互调用。当一个服务发生故障或延迟时,调用链上的其他服务可能会受到影响,导致整个系统出现级联故障。为了解决这个问题,引入了Circuit Breaker模式。Circuit Breaker模式包含三个状态:关闭、开启和半开。

Circuit Breaker是一套规范和接口,真正落地实现的是Resilience4J

Resilience4J是一个用于构建可靠和弹性应用程序的Java库。它提供了一套轻量级的容错模式和工具,帮助开发人员更容易地实现应用程序的弹性。 需要Java17

//核心模块
resilience4j-circuitbreaker: 熔断		√
resilience4j-ratelimiter: 限流			√
resilience4j-bulkhead: 舱壁				√
resilience4j-retry: ⾃动重试
resilience4j-cache: 结果缓存
resilience4j-timelimiter: 超时处理

1.熔断
熔断器有三种状态:关闭状态(正常状态)、开启状态(不允许通过)和半开状态。

failure-rate-threshold 
以百分比配置失败率峰值

sliding-window-type 
断路器的滑动窗口期类型
可以基于“次数”(COUNT_BASED)或者“时间”(TIME_BASED)进行熔断,默认是COUNT_BASED。

sliding-window-size
若COUNT_BASED,则10次调用中有50%失败(即5次)打开熔断断路器;
若为TIME_BASED则,此时还有额外的两个设置属性,含义为:在N秒内(sliding-window-size)100%(slow-call-rate-threshold)的请求超过N秒(slow-call-duration-threshold)打开断路器。

slowCallRateThreshold
以百分比的方式配置,断路器把调用时间大于slowCallDurationThreshold的调用视为慢调用,当慢调用比例大于等于峰值时,断路器开启,并进入服务降级。

slowCallDurationThreshold
配置调用时间的峰值,高于该峰值的视为慢调用。

permitted-number-of-calls-in-half-open-state
运行断路器在HALF_OPEN状态下时进行N次调用,如果故障或慢速调用仍然高于阈值,断路器再次进入打开状态。

minimum-number-of-calls
在每个滑动窗口期样本数,配置断路器计算错误率或者慢调用率的最小调用数。比如设置为5意味着,在计算故障率之前,必须至少调用5次。如果只记录了4次,即使4次都失败了,断路器也不会进入到打开状态。

wait-duration-in-open-state
从OPENHALF_OPEN状态需要等待的时间

配置 滑动窗口类型: COUNT_BASED

//在8001端口新增controller
@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();
    }
}


//在通用接口添加
/**
     * Resilience4j CircuitBreaker 的例子
     * @param id
     * @return
     */
    @GetMapping(value = "/pay/circuit/{id}")
    public String myCircuit(@PathVariable("id") Integer id);



//在80端口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>


//改yml文件
server:
  port: 80

spring:
  application:
    name: cloud-consumer-openfeign-order
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true #优先使用服务ip进行注册
        service-name: ${spring.application.name}
    openfeign:
      client:
        config:
          default:
            #连接超时时间
            connectTimeout: 3000          #3秒
            #读取超时时间
            readTimeout: 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日志以什么级别监控哪个接口        OpenFeign内容
logging:
  level:
    com:
      dc:
        cloud:
          apis:
            PayFeignApi: debug

# 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 #从OPENHALF_OPEN状态需要等待的时间
        permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。在半开状态下,CircuitBreaker将允许最多permittedNumberOfCallsInHalfOpenState个请求通过,如果其中有任何一个请求失败,CircuitBreaker将重新进入开启状态。
        recordExceptions:
          - java.lang.Exception
    instances:
      cloud-payment-service:
        baseConfig: default


//在80端口新增OrderCircuitController类
@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ㄒ)/~~";
    }
}

滑动窗口类型: TIME_BASED

//把之前的基于COUNT_BASED的yml配置注释掉注释掉,改为新的yml配置
# 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 #从OPENHALF_OPEN状态需要等待的时间
        recordExceptions:
          - java.lang.Exception
    instances:
      cloud-payment-service:
        baseConfig: default 

2.舱壁隔离
Spring Cloud的舱壁隔离是一种微服务架构中的隔离机制,用于防止服务之间的相互影响和故障扩散。舱壁隔离通过在不同的线程池或线程组中执行不同的服务实例,将它们隔离开来。
Spring Cloud提供了两种常用的舱壁隔离实现方法:线程池隔离信号量隔离

1.信号隔离

//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();
}

//在PayFeignApi接口新增方法
/**
 * Resilience4j Bulkhead 的例子
 * @param id
 * @return
 */
@GetMapping(value = "/pay/bulkhead/{id}")
public String myBulkhead(@PathVariable("id") Integer id);

//在feign80端口新增依赖
<!--resilience4j-bulkhead-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-bulkhead</artifactId>
</dependency>

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

//在OrderCircuitController类新增方法
/**
 *(船的)舱壁,隔离
 * @param id
 * @return
 */
@GetMapping(value = "/feign/pay/bulkhead/{id}")												//Bulkhead.Type.SEMAPHORE类型
@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ㄒ)/~~";
}

线程池隔离 FixedThreadPoolBulkhead 固定线程池舱壁

//修改yml配置
####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 新启线程和原来主线程脱离

 //修改controller
 @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ㄒ)/~~";
    }

    /**
     *(船的)舱壁,隔离
     * @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ㄒ)/~~";
//    }

    //type = Bulkhead.Type.THREADPOOL
    @GetMapping(value = "/feign/pay/bulkhead/{id}")
    @Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadFallback",type = Bulkhead.Type.THREADPOOL)
    public CompletableFuture<String> myBulkheadTHREADPOOL(@PathVariable("id") Integer id)
    {
        System.out.println(Thread.currentThread().getName()+"\t"+"开始进入");
        try{
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"\t"+"准备离开");
        return CompletableFuture.supplyAsync(() -> payFeignApi.myBulkhead(id)+"\t"+"Bulkhead.Type.THREADPOOL");
    }
    public CompletableFuture<String> myBulkheadFallbackTHREADPOOL(Throwable t)
    {
        return CompletableFuture.supplyAsync(() -> "Bulkhead.Type.THREADPOOL,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~");
    }
}

3.限流
几种常见的限流算法:
1.固定窗口限流算法(Fixed Window Algorithm):在固定时间窗口内,限制请求的数量。例如,每秒最多允许处理100个请求。

2.滑动窗口限流算法(Sliding Window Algorithm):在滑动的时间窗口内,限制请求的数量。滑动窗口可以是固定长度的时间段,也可以是固定数量的请求。根据滑动窗口的长度和滑动步长,可以调整限流的精度和准确性。

3.漏桶算法(Leaky Bucket Algorithm):将请求以固定的速率放入一个容器(桶)中,如果桶已满,则丢弃请求。这种算法可以平滑请求的流量,避免突发的请求压力。

4.令牌桶算法(Token Bucket Algorithm):桶中有一定数量的令牌,每个请求需要消耗一个令牌才能通过。每秒钟会向桶中添加一定数量的令牌。当桶中的令牌用完时,请求将被限制。 【常用】

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

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

//导入pom依赖
<!--resilience4j-ratelimiter-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-ratelimiter</artifactId>
</dependency>

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

//新增OrderCircuitController类方法
    @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ㄒ)/~~";
    }
举报

相关推荐

0 条评论