06-CircuitBreaker断路器

1、介绍

Spring Cloud Circuit breaker provides an abstraction across different circuit breaker implementations. It provides a consistent API to use in your applications allowing you the developer to choose the circuit breaker implementation that best fits your needs for your app.

Spring Cloud断路器提供了一个跨不同断路器的抽象实现。它提供了在您的应用程序中使用的一致的API,允许您的开发人员选择最适合您的应用程序需要的断路器实现。

CircuitBreaker通过具有三种正常状态的有限状态机实现:CLOSED,OPEN和HALF_OPEN以及两个特殊状态DISABLED和FORCED_OPEN。当熔断器关闭时,所有的请求都会通过熔断器。如果失败率超过设定的阈值,熔断器就会从关闭状态转换到打开状态,这时所有的请求都会被拒绝。当经过一段时间后,熔断器会从打开状态转换到半开状态,这时仅有一定数量的请求会被放入,并重新计算失败率,如果失败率超过阈值,则变为打开状态,如果失败率低于阈值,则变为关闭状态。

06-CircuitBreaker断路器

支持断路器实现有以下几种

  • Netfix Hystrix (Spring Cloud官方已经不予支持)
  • Resilience4J (支持)
  • Sentinel
  • Spring Retry

2、快速开始

pom.xml
maven依赖配置文件,如下:


    4.0.0

        org.springframework.boot
        spring-boot-starter-parent
        2.6.4

    com.mindasoft
    spring-cloud-circuitbreaker-consumer
    0.0.1-SNAPSHOT
    spring-cloud-circuitbreaker-consumer
    Demo project for Spring Boot

        UTF-8
        UTF-8
        1.8
        2021.0.1

            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client

            org.springframework.cloud
            spring-cloud-starter-circuitbreaker-resilience4j

            org.springframework.boot
            spring-boot-starter-web

            org.springframework.boot
            spring-boot-starter-test
            test

                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import

                org.springframework.boot
                spring-boot-maven-plugin

            nexus-aliyun
            Nexus aliyun
            https://maven.aliyun.com/repository/public
            default

                false

                true

            nexus-aliyun
            Nexus aliyun
            https://maven.aliyun.com/repository/public

                false

                true

比上个项目多了如下配置:


            org.springframework.cloud
            spring-cloud-starter-circuitbreaker-resilience4j

使用resilience4j 实现断路器。

application.xml

server.port=9004

服务注册中心地址
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

服务名称
spring.application.name=circuitbreaker-customer

SpringCloudCircuitBreakerConsumerApplication

package com.mindasoft;

import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;

@EnableDiscoveryClient // Eureka Discovery Client 标识
@SpringBootApplication
public class SpringCloudCircuitBreakerConsumerApplication {

    // 开启负载均衡的功能
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public Customizer defaultCustomizer() {
        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
                .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(3)).build())
                .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
                .build());
    }

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

}

ConsumerController

package com.mindasoft.consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * Created by huangmin on 2017/11/10 14:50.

 */
@RestController
public class ConsumerController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerController.class);

    @Autowired
    private RestTemplate restTemplate; // HTTP 访问操作类
    @Autowired
    private CircuitBreakerFactory circuitBreakerFactory;

    @RequestMapping("/hello")
    public String hello() {
        String providerMsg = circuitBreakerFactory.create("hello")
                .run(() -> restTemplate.getForObject("http://PROVIDER-SERVICE/hello", String.class),
                        throwable -> "CircuitBreaker fallback msg");

        return "Hello,I'm Customer! msg from provider :  " + providerMsg;
    }

}

启动eureka server和本项目,访问http://127.0.0.1:9004/hello
页面打印如下:

Hello,I'm Customer! msg from provider :

CircuitBreaker fallback msg

说明进入到了断路throwable 处理逻辑。

3、Resilience4J介绍

Resilience4j是一款轻量级,易于使用的容错库,其灵感来自于Netflix Hystrix,但是专为Java 8和函数式编程而设计。轻量级,因为库只使用了Vavr,它没有任何其他外部依赖下。相比之下,Netflix Hystrix对Archaius具有编译依赖性,Archaius具有更多的外部库依赖性,例如Guava和Apache Commons Configuration。

Resilience4j提供了以下的核心模块和拓展模块:

核心模块:

  • resilience4j-circuitbreaker: Circuit breaking
  • resilience4j-ratelimiter: Rate limiting
  • resilience4j-bulkhead: Bulkheading
  • resilience4j-retry: Automatic retrying (sync and async)
  • resilience4j-cache: Result caching
  • resilience4j-timelimiter: Timeout handling

Resilience4j记录请求状态的数据结构和Hystrix不同,Hystrix是使用滑动窗口来进行存储的,而Resilience4j采用的是Ring Bit Buffer(环形缓冲区)。Ring Bit Buffer在内部使用BitSet这样的数据结构来进行存储,BitSet的结构如下图所示:

06-CircuitBreaker断路器

每一次请求的成功或失败状态只占用一个bit位,与boolean数组相比更节省内存。BitSet使用long[]数组来存储这些数据,意味着16个值(64bit)的数组可以存储1024个调用状态。

计算失败率需要填满环形缓冲区。例如,如果环形缓冲区的大小为10,则必须至少请求满10次,才会进行故障率的计算,如果仅仅请求了9次,即使9个请求都失败,熔断器也不会打开。但是CLOSE状态下的缓冲区大小设置为10并不意味着只会进入10个 请求,在熔断器打开之前的所有请求都会被放入。

当故障率高于设定的阈值时,熔断器状态会从由CLOSE变为OPEN。这时所有的请求都会抛出CallNotPermittedException异常。当经过一段时间后,熔断器的状态会从OPEN变为HALF_OPEN,HALF_OPEN状态下同样会有一个Ring Bit Buffer,用来计算HALF_OPEN状态下的故障率,如果高于配置的阈值,会转换为OPEN,低于阈值则装换为CLOSE。与CLOSE状态下的缓冲区不同的地方在于,HALF_OPEN状态下的缓冲区大小会限制请求数,只有缓冲区大小的请求数会被放入。

除此以外,熔断器还会有两种特殊状态:DISABLED(始终允许访问)和FORCED_OPEN(始终拒绝访问)。这两个状态不会生成熔断器事件(除状态装换外),并且不会记录事件的成功或者失败。退出这两个状态的唯一方法是触发状态转换或者重置熔断器。

熔断器关于线程安全的保证措施有以下几个部分:

  • 熔断器的状态使用AtomicReference保存的
  • 更新熔断器状态是通过无状态的函数或者原子操作进行的
  • 更新事件的状态用synchronized关键字保护
  • 意味着同一时间只有一个线程能够修改熔断器状态或者记录事件的状态。

使用注解配置@CircuitBreaking

首先在POM文件中添加:


            org.springframework.boot
            spring-boot-starter-aop

spring-boot-starter-aop必须添加,否则annotation不会生效。

application.xml

server.port=9004

服务注册中心地址
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

服务名称
spring.application.name=circuitbreaker-customer

断路器配置
resilience4j.circuitbreaker:
  configs:
    default:
      ringBufferSizeInClosedState: 1 # 熔断器关闭时的缓冲区大小
      ringBufferSizeInHalfOpenState: 1 # 熔断器半开时的缓冲区大小
      waitDurationInOpenState: 60000 # 熔断器从打开到半开需要的时间
      failureRateThreshold: 100 # 熔断器打开的失败阈值
      eventConsumerBufferSize: 5 # 事件缓冲区大小
      registerHealthIndicator: true # 健康监测
      automaticTransitionFromOpenToHalfOpenEnabled: true # 是否自动从打开到半开,不需要触发
  instances:
    providerService: # 断路器策略的命名
      baseConfig: default #继承基础配置

直接在配置文件中,添加断路器配置。JavaConfig模式的配置我们就去掉了。

SpringCloudCircuitBreakerConsumerApplication

package com.mindasoft;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient // Eureka Discovery Client 标识
@SpringBootApplication
public class SpringCloudCircuitBreakerConsumerApplication {

    // 开启负载均衡的功能
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

}

我们将远程调用服务,独立出来。
ProviderService

public interface ProviderService {

    String hello();
}

package com.mindasoft.serivce;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * 功能描述:
 *
 * @author huangmin
 * @date 2022/3/1 10:04 上午
 */
@Service
public class ProviderServiceImpl implements ProviderService {
    @Autowired
    private RestTemplate restTemplate; // HTTP 访问操作类

    @Override
    @CircuitBreaker(name = "providerService", fallbackMethod = "fallback")
    public String hello() {
        String providerMsg = restTemplate.getForObject("http://PROVIDER-SERVICE/hello", String.class);
        return providerMsg;
    }

    private String fallback(Exception ex) {
        return "Recovered: " + ex.toString();
    }

}

ConsumerController也需要修改一下:

@RestController
public class ConsumerController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerController.class);

    @Autowired
    private ProviderService providerService;

    @RequestMapping("/hello")
    public String hello() {

        String providerMsg = providerService.hello();
        return "Hello,I'm Customer! msg from provider :  " + providerMsg;
    }

}

改造完成,然后启动服务,访问hello时,得到以下结果:

第一次访问:
Hello,I'm Customer! msg from provider :

Recovered: java.lang.IllegalStateException: No instances available for PROVIDER-SERVICE

#第二次访问:
Hello,I'm Customer! msg from provider :

Recovered: io.github.resilience4j.circuitbreaker.CallNotPermittedException: CircuitBreaker 'providerService' is OPEN and does not permit further calls

其他功能

resilience4j还有其他功能,如断路器一样使用注解@Retry、@Bulkhead、@RateLimiter、@Timelimiter,简单的配置如下。

resilience4j.retry:
    configs:
        default:
            maxAttempts: 3
            waitDuration: 100
    instances:
        providerService:
            baseConfig: default

resilience4j.bulkhead:
    configs:
        default:
            maxConcurrentCalls: 100
    instances:
        providerService:
            maxWaitDuration: 10ms
            maxConcurrentCalls: 20

resilience4j.thread-pool-bulkhead:
    configs:
        default:
            maxThreadPoolSize: 4
            coreThreadPoolSize: 2
            queueCapacity: 2
    instances:
        providerService:
            baseConfig: default
        providerServiceB:
            maxThreadPoolSize: 1
            coreThreadPoolSize: 1
            queueCapacity: 1

resilience4j.ratelimiter:
    configs:
        default:
            registerHealthIndicator: false
            limitForPeriod: 10
            limitRefreshPeriod: 1s
            timeoutDuration: 0
            eventConsumerBufferSize: 100
    instances:
        providerService:
            baseConfig: default
        providerServiceB:
            limitForPeriod: 6
            limitRefreshPeriod: 500ms
            timeoutDuration: 3s

resilience4j.timelimiter:
    configs:
        default:
            cancelRunningFuture: false
            timeoutDuration: 2s
    instances:
        providerService:
            baseConfig: default

Original: https://www.cnblogs.com/mpiter/p/15978947.html
Author: 技术那点事儿
Title: 06-CircuitBreaker断路器

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/581106/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java学习 (13)Java流程控制篇(04)break & continue & goto

    break & continue break 语法实例 continue 语法实例 break 与 continue 的差异 goto 练习 参考视频(狂神说Java) b…

    Java 2023年6月8日
    084
  • 7-枚举和注解

    枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类 当需要定义一组常量时,强烈建议使用枚举类 如果枚举类中只一个对象,则可以作为单例模式的实现方式。 1.如何自定义枚举类…

    Java 2023年6月7日
    070
  • 1.BigDecimal用法

    1.BigDecimal用法详解 一、简介Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double…

    Java 2023年6月9日
    0132
  • JavaSE笔记

    文末有Gitee链接,记得star哦课程整体内容概述 第一部分:编程语言核心结构主要知识点:变量、基本语法、分支、循环、数组、第二部分:Java面向对象的核心逻辑主要知识点:OOP…

    Java 2023年6月13日
    070
  • Spring Security过滤器链分析-初始化流程(8)

    过滤器链分析 提起Spring Security的实现原理,很多读者都会想到过滤器链。因为Spring Security中的所有功能都是通过过滤器来实现的,这些过滤器组成一个完整的…

    Java 2023年6月13日
    086
  • Spring配置 bean

    在 xml 文件中通过 bean 节点来配置 beanid:Bean 的名称。在 IOC 容器中必须是唯一的若 id 没有指定,Spring 自动将权限定性类名作为 Bean 的名…

    Java 2023年5月30日
    092
  • 快速掌握Java8 Stream函数式编程技巧

    函数式编程优势 “函数第一位”,即函数可以出现在任何地方。 可以把函数作为参数传递给另一个函数,还可以将函数作为返回值。 让代码的逻辑更清晰更优雅。 减少了…

    Java 2023年5月29日
    071
  • iBoxDB的学习与使用

    1. 引言 说说iBoxDB的优点: 1)无需安装,不像其他数据库比如MongoDB, MySQL 需要安装。iBOXDB只需要某个目录存放最终的数据即可。完全就像操作本地文件一 …

    Java 2023年6月5日
    060
  • 你还在用BeanUtils进行对象属性拷贝?

    在做业务的时候,为了隔离变化,我们会将 DAO查询出来的 DO和对前端提供的 DTO隔离开来。大概 90%的时候,它们的结构都是类似的;但是我们很不喜欢写很多冗长的 b.setF1…

    Java 2023年6月6日
    077
  • Nginx $remote_addr和$proxy_add_x_forwarded_for变量详解

    $remote_addr 代表客户端IP。注意,这里的客户端指的是直接请求Nginx的客户端,非间接请求的客户端。假设用户请求过程如下: 用&#x62…

    Java 2023年6月16日
    080
  • MQTT( MQ Telemetry Transport)

    MQTT MQTT 协议 是基于发布/订阅模式的物联网通信协议,凭借简单易实现、支持 QoS、报文小等特点,占据了物联网协议的半壁江山 https://www.emqx.cn/mq…

    Java 2023年5月30日
    0191
  • Day10

    package com.oop;import java.io.IOException;//Demo1 类public class Demo1 { //main public sta…

    Java 2023年6月5日
    094
  • JAVAWEB开发之JSTL标签库的使用、 自己定义EL函数、自己定义标签(带属性的、带标签体的)

    JSTL JSTL简单介绍: JSTL的全称:JSP Standard Tag Library,JSP标准标签库 JSTL的作用: 提供给Java Web开发者一个标准通用的标签函…

    Java 2023年5月29日
    077
  • Invalid bound statement (not found)出现原因和解决方法

    Invalid bound statement (not found)出现原因和解决方法 前言: 想必各位小伙伴在码路上经常会碰到奇奇怪怪的事情,比如出现Invalid bound…

    Java 2023年6月15日
    059
  • LeetCode剑指Offer刷题总结(一)

    LeetCode过程中值得反思的细节 以下题号均指LeetCode剑指offer题库中的题号 本文章将每周定期更新,当内容达到10题左右时将会开下一节。 二维数组越界问题04 pu…

    Java 2023年6月7日
    075
  • 面向对象ooDay1

    如何创建类?如何创建对象?如何访问成员? package ooday01; //学生类 public class Student { //成员变量—-对象的属性 String …

    Java 2023年6月13日
    083
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球