resilience4j筆記


0 環境

  • 操作系統:win10
  • 編輯器:idea
  • springcloud版本:H版

1 前言

Resilience4j 是受Netflix的Hysrix項目啟發,專門為Java 8 和函數式編程設計的輕量級容錯框架。

  • 主要功能
  • CircuitBreaker(熔斷器)
  • RateLimiter(限流)
  • Retry(請求重試)
  • 限時
  • 緩存
  • 信號量的隔離

2 初見

這是個maven項目 在test中 先體驗一下斷路器 限流 請求重試

2.1 maven項目的創建

  • 新建maven子模塊 ...下一步即可

2.2 導入pom.xml依賴

  • 為了省事 我直接全cv過來了
<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-circuitbreaker</artifactId>
            <version>0.13.2</version>
        </dependency>

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

        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-retry</artifactId>
            <version>0.13.2</version>
        </dependency>

2.3 創建測試類

在這里插入圖片描述

2.4 CircuitBreaker(熔斷器)

  • 正常的斷路器
// 正常斷路器
    @Test
    public void test(){
        // 獲取一個CircuitBreakerRegistry實例 可調用ofDefaults獲取實例(可自定義屬性)
        CircuitBreakerRegistry registry = CircuitBreakerRegistry.ofDefaults();
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                // 默認50 故障率閥值百分比 超過這個閥值 斷路器就會打開
                .failureRateThreshold(50)
                // 斷路器保持打開時間 在到達設置時間后 斷路器會進入到half open狀態
                .waitDurationInOpenState(Duration.ofMillis(1000))
                // 當斷路器處於half open狀態時 環形緩沖區的大小
                .ringBufferSizeInOpenState(2)
                .ringBufferSizeInClosedState(2)
                .build();

        // 2種CircuitBreaker調用方式
        CircuitBreakerRegistry registry1 = CircuitBreakerRegistry.of(config);
        CircuitBreaker breaker = registry1.circuitBreaker("learn");

        CircuitBreaker breaker1 = registry.circuitBreaker("learn1", config);

        //
        CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(breaker, () -> "hello resilience4j");
        Try<String> map = Try.of(supplier)
                .map(v -> v + " you are fish");
        System.out.println(map.isSuccess());
        System.out.println(map.get());

        System.out.println("<========================================>");

        CheckedFunction0<String> supplier1 = CircuitBreaker.decorateCheckedSupplier(breaker1, () -> "hello resilience4j");
        Try<String> map1 = Try.of(supplier)
                .map(v -> v + " you are fish");
        System.err.println(map1.isSuccess());
        System.err.println(map1.get());
    }

在這里插入圖片描述

  • 異常斷路器
	// 一個異常的斷路器
    // 需要手動調用2次onError --> ringBufferSizeInClosedState(2) --> 當有2條故障數據才會統計 --> 斷路器才會開啟
    @Test
    public void test1(){
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                // 默認50 故障率閥值百分比 超過這個閥值 斷路器就會打開
                .failureRateThreshold(50)
                // 斷路器保持打開的時間 在到達設置的時間之后 斷路器會進入到half open狀態
                .waitDurationInOpenState(Duration.ofMillis(1000))
                // 當斷路器處於half open狀態時(環形緩沖區大小)
                .ringBufferSizeInClosedState(2)
                .build();

        CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
        CircuitBreaker breaker1 = registry.circuitBreaker("learn");
        // 獲取斷路器的狀態
        System.out.println(breaker1.getState());
        breaker1.onError(0, new RuntimeException());
        System.out.println(breaker1.getState());
        breaker1.onError(0, new IllegalArgumentException());
        System.out.println(breaker1.getState());
        CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(breaker1, () -> "hello resilience4j");
        Try<String> map = Try.of(supplier)
                .map(v -> v + " hello kitty");
        System.out.println("<========================================>");

        System.out.println("是否成功:" + map.isSuccess());
        System.out.println("獲取值:" + map.get());

    }

在這里插入圖片描述

2.5 限流

 // 限流 和斷路器類似
    @Test
    public void test2(){
        RateLimiterConfig build = RateLimiterConfig.custom()
                // 閾值刷新的時間
                .limitRefreshPeriod(Duration.ofMillis(1000))
                // 限制頻次
                .limitForPeriod(3)
                // 冷卻時間
                .timeoutDuration(Duration.ofMillis(1000))
                .build();
        RateLimiter limiter = RateLimiter.of("learning", build);

        CheckedRunnable runnable = RateLimiter.decorateCheckedRunnable(limiter, () -> {
            System.out.println(new Date());
        });

        // 執行4次
        Try.run(runnable)
                .andThenTry(runnable)
                .andThenTry(runnable)
                .andThenTry(runnable)
                .onFailure(t -> System.out.println(t.getMessage()));
    }

在這里插入圖片描述

2.6 請求重試

// 請求重試
    @Test
    public void test3(){
        RetryConfig config = RetryConfig.custom()
                // 重試次數
                .maxAttempts(5)
                // 重試間隔
                .waitDuration(Duration.ofMillis(500))
                // 重試異常
                .retryExceptions(RuntimeException.class)
                .build();

        Retry retry = Retry.of("leaning1", config);
        Retry.decorateRunnable(retry, new Runnable() {

            int count = 0;

            // 重試功能開啟后 執行run方法 若拋出異常 會自動觸發重試功能
            @Override
            public void run() {
                if (count++ < 4){
                    System.out.println(count);
                    throw new RuntimeException();
                }
            }
        }).run();
    }

在這里插入圖片描述

2.7 小結

  • 方法調用很類似
  • 斷路器 --> 滿足條件 開啟斷路器
  • 限流 --> 類似限制每個人領多少個口罩
  • 請求重試 --> 類似n顧茅廬

3 再見之請求重試

  • 請求重試 微服務版

3.1 創建springboot項目

在這里插入圖片描述

3.2 請求重試pom.xml配置

還需要添加resilience4j相關組件 需要排除未配置的組件 不然會報錯呢

<dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>1.3.1</version>
            <exclusions>
                <!-- 沒有配置的 先排除 不然會報錯 -->
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-circuitbreaker</artifactId>
                </exclusion>

                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-ratelimiter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-bulkhead</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-timelimiter</artifactId>
                </exclusion>
            </exclusions>

3.3 請求重試yml配置

// 為了避免寫錯 直接從這個RetryConfigurationProperties類里找到InstanceProperties類里的屬性cv
public static class InstanceProperties {
        @Nullable
        private Duration waitDuration;
        @Nullable
        private Integer maxRetryAttempts;
        @Nullable
        private Class<? extends Predicate<Throwable>> retryExceptionPredicate;
        @Nullable
        private Class<? extends Predicate<Object>> resultPredicate;
        @Nullable
        private Class<? extends Throwable>[] retryExceptions;
        @Nullable
        private Class<? extends Throwable>[] ignoreExceptions;
        @Nullable
        private Integer eventConsumerBufferSize;
        @Nullable
        private Boolean enableExponentialBackoff;
        private Double exponentialBackoffMultiplier;
        @Nullable
        private Boolean enableRandomizedWait;
        private Double randomizedWaitFactor;
        @Nullable
        private String baseConfig;
resilience4j:
  retry:
    retry-aspect-order: 133 # 表示Retry優先級(級別高於比如ratelimiter bulkhead timelimiter) 值越小 -> 越大
    backends:
      # 設置組名
      retryA:
        # 對比之前的案例
        # 重試次數
        maxRetryAttempts: 4
        # 重試等待
        waitDuration: 400
        # 間隔乘數(場景: 正好每次間隔為1的時候卡頓 它就有用了 間隔就變了 例如 1 1.1 1.21....)
        exponentialBackoffMultiplier: 1.1
        retryExceptions:
          - java.lang.RuntimeException

spring:
  application:
    name: resilience4j

server:
  port: 7000

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

3.4 請求重試application啟動類里配置

	@Bean
	//    @LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }

3.5 eureka provider的配置

** 聲明一下 用的是eureka server(基本不變)和provider 和resilience4j的請求重試 **

@GetMapping("/hello3")
    public String hello3(){
        String result = "hello provider:" + port;
        // 為了檢測重試策略是否生效
        System.out.println("print hello3 func result: " + result);
        // 加一個錯誤 測試重試策略
        int i = 1 / 0;
        return result;
    }

3.6 請求重試的配置

@RestController
public class HelloController {

    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String hello(){
        return helloService.hello();
    }
}


@Service
// 在這里使用重試策略 yml中backends下配置的
@Retry(name = "retryA")
public class HelloService {
    @Autowired
    RestTemplate restTemplate;
    
    public String hello(){ 
        return restTemplate.getForObject("http://127.0.0.1:1300/hello3", String.class);
    }
}

3.7 啟動運行

啟動eureka server和provider 和請求重試組件

  1. 訪問http://localhost:7000/hello
    在這里插入圖片描述
  2. 查看后台provider 在這里插入圖片描述

3.8 小結

  • 總結
    • 通過provider提供一個異常 yml配置請求重試4次(重試等待時間 時間間隔 重試異常..) 啟動驗證是否成功
    • pom.xml配置那個resilience4j組件 需要注意配置哪個組件 在里面移除哪個組件 若移除的組件未配置 會報錯

4 再見之斷路器

4.1 pom.xml配置

特別說明版本需要改為 1.2.0 不然會報錯 雖然之前的請求重試沒問題

<dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>1.2.0</version>
            <exclusions>
                <!-- 沒有配置的 先排除 不然會報錯 -->
<!--                <exclusion>-->
<!--                    <groupId>io.github.resilience4j</groupId>-->
<!--                    <artifactId>resilience4j-circuitbreaker</artifactId>-->
<!--                </exclusion>-->

                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-ratelimiter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-bulkhead</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-timelimiter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

4.2 yml的配置

resilience4j:
  retry:
    retry-aspect-order: 133 # 表示Retry優先級(級別高於比如ratelimiter bulkhead timelimiter) 值越小 -> 越大
    backends:
      # 設置組名
      retryA:
        # 對比之前的案例
        # 重試次數
        maxRetryAttempts: 4
        # 重試等待
        waitDuration: 400
        # 間隔乘數(場景: 正好每次間隔為1的時候卡頓 它就有用了 間隔就變了 例如 1 1.1 1.21....)
        exponentialBackoffMultiplier: 1.1
        retryExceptions:
          - java.lang.RuntimeException
  # 和之前的maven類似
  circuitbreaker:
    instances:
      nba:
        ringBufferSizeInHalfOpenState: 4
        ringBufferSizeInClosedState:  4
        waitInterval: 4000
        recordExceptions:
          - org.springframework.web.client.HttpServerErrorException
    # 要比上面的值小(先執行當前斷路器)
    circuit-breaker-aspect-order: 132

spring:
  application:
    name: resilience4j

server:
  port: 7000

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

4.3 后端配置

之前application啟動類中 已經配置好了RestTemplate controller層沿用之前 只是修改service層

@Service
// 在這里使用重試策略 yml中backends下配置的
//@Retry(name = "retryA")
// name屬性指定CircuitBreaker中yml的配置 fallbackMethod屬性指定服務降級方法
@CircuitBreaker(name = "nba", fallbackMethod = "error")
public class HelloService {
    @Autowired
    RestTemplate restTemplate;

    public String hello(){
        return restTemplate.getForObject("http://127.0.0.1:1300/hello3", String.class);
    }

    // 服務降級方法中 不加參數Throwable 會報錯提示缺少Throwable 要添加異常參數
    public String error(Throwable throwable){
        return "error";
    }
}

4.4 啟動項目

和請求重試的啟動方式是一樣的(重啟/啟動) 訪問網址
在這里插入圖片描述

4.5 小結

  • 和斷路器類似 需要注意的項
    • pom.xml中配置的版本號 不然會報錯
    • 優先級值的配置 決定誰先運行
    • 在service層 配置斷路器 降級方法 需要添加異常參數 否則啟動會報錯缺陷異常

5 再見之限流

5.1 provider

我們要限流 肯定是要從上層限制(比如從廠家那里限制貨品發放量 類似在provider 提供者上進行限制)

  • 在provider中添加限流依賴
<dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>1.2.0</version>
            <exclusions>
                <!--  沒有配置的 先排除 不然會報錯 -->
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-circuitbreaker</artifactId>
                </exclusion>

                <!--ratelimiter依賴移除-->
<!--                <exclusion>-->
<!--                    <groupId>io.github.resilience4j</groupId>-->
<!--                    <artifactId>resilience4j-ratelimiter</artifactId>-->
<!--                </exclusion>-->
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-bulkhead</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.github.resilience4j</groupId>
                    <artifactId>resilience4j-timelimiter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
  • yml配置
# 每秒鍾處理2個請求
resilience4j:
  ratelimiter:
    limiters:
      rltA:
        limit-for-period: 2
        limit-refresh-period: 1s
        timeout-duration: 1s
  • controller層配置
@RestController
public class HelloController {


    @Value("${server.port}")
    Integer port;

    @GetMapping("/hello")
    @RateLimiter(name = "rltA")
    public String hello(){
        System.out.println(new Date());
        return "hello provider:" + port;
    }
}    

5.2 consumer(消費者)

@RestController
public class HelloController {

    @Autowired
    HelloService helloService;

    @GetMapping("/hello1")
    public String hello1(){
        return helloService.hello1();
    }
}


@Service
public class HelloService {
    @Autowired
    RestTemplate restTemplate;
	
	// 限流配置
    public String hello1(){
        for (int i = 0; i < 5; i++) {
            restTemplate.getForObject("http://127.0.0.1:1300/hello", String.class);
        }
        return "success ratA";
    }
}

5.3 運行

在這里插入圖片描述在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM