輕松掌握!Spring Boot 集成 Resilience4j 實現斷路器的全流程實戰
在現代分布式系統架構中,服務之間的通信非常頻繁,尤其是微服務架構下,每個微服務都會依賴其他服務的響應。雖然這種架構能夠有效地提高系統的擴展性和靈活性,但也帶來了一些問題,比如網絡延遲、依賴的服務不可用、超時等。為了避免整個系統因為某個服務不可用而崩潰,我們可以使用 斷路器模式 來防止這種“雪崩效應”的發生。
斷路器模式(Circuit Breaker Pattern)作為一種保護機制,可以幫助我們監控和控制外部服務的調用。在服務出現故障時,斷路器可以快速響應并阻止后續調用,從而避免不必要的等待和資源消耗。本文將結合代碼示例,講解如何在 Spring Boot 項目中使用 Resilience4j 實現斷路器,并展示如何在前后端代碼中進行交互,前端部分使用 Thymeleaf 模板引擎,結合 jQuery 和 Bootstrap 實現。
斷路器模式簡介
斷路器模式 是應對外部服務故障的一種保護機制。它的核心思想是,當某個外部服務調用頻繁失敗時,不再繼續嘗試調用該服務,而是直接返回一個預設的結果或執行一個備用邏輯(即回退方法)。斷路器模式通常包含以下三種狀態:
- 關閉狀態 (Closed):當服務正常工作時,斷路器處于關閉狀態,所有請求都會直接通過并調用目標服務。
- 打開狀態 (Open):當檢測到服務連續多次失敗,斷路器會進入打開狀態,此時所有請求都會被快速失敗,直接觸發回退方法。
- 半開狀態 (Half-Open):經過一段時間后,斷路器會自動嘗試允許少量請求通過,如果這些請求成功,斷路器會回到關閉狀態;否則,繼續保持打開狀態。
這種機制能夠有效防止系統因為某個服務的不可用而產生的資源浪費和響應延遲。
運行效果:
圖片
若想獲取項目完整代碼以及其他文章的項目源碼,且在代碼編寫時遇到問題需要咨詢交流,歡迎加入下方的知識星球。
引入依賴 (pom.xml)
首先,我們需要在 pom.xml 文件中引入相關的依賴。這里包括 Spring Boot、Resilience4j、Lombok 以及用于模板渲染的 Thymeleaf 依賴。
<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>circuit-breaker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>circuit-breaker</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Resilience4j 斷路器 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Thymeleaf 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件 (application.yml)
接下來,我們需要在 application.yml 中為 Resilience4j 斷路器配置相關參數。這些參數用于定義斷路器的行為,包括滑動窗口的大小、失敗率閾值、斷路器打開狀態的等待時間等。
resilience4j:
circuitbreaker:
configs:
default:
slidingWindowSize: 10 # 增大滑動窗口以計算多個請求的失敗率
failureRateThreshold: 50 # 設置更高的失敗率閾值,例如 50%
waitDurationInOpenState: 10000 # 打開狀態持續時間 10 秒
permittedNumberOfCallsInHalfOpenState: 3 # 半開狀態下允許通過的請求數量
minimumNumberOfCalls: 5 # 最少需要 5 個請求才能計算失敗率
automaticTransitionFromOpenToHalfOpenEnabled: true
instances:
myService:
baseConfig: default
timeout:
default:
timeoutDuration: 2s # 設置超時時間為 2 秒
timeoutDuration:請求超過 2 秒沒有返回時會觸發超時異常。
failureRateThreshold:將失敗率設置為 50%,這樣只要一半的請求失敗,斷路器就會打開。
minimumNumberOfCalls:設置為 5,以確保在少量請求中也能計算失敗率
讀取配置類 (@ConfigurationProperties)
我們可以使用 @ConfigurationProperties 注解來讀取配置文件中的斷路器相關配置,并通過 Lombok 自動生成類的 getter 和 setter 方法。
package com.icoderoad.circuit.breaker.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
@ConfigurationProperties(prefix = "resilience4j.circuitbreaker")
public class CircuitBreakerProperties {
private CircuitBreakerConfig configs;
private CircuitBreakerInstance instances;
}
package com.icoderoad.circuit.breaker.config;
import lombok.Data;
@Data
class CircuitBreakerConfig {
private DefaultConfig defaultConfig;
}
package com.icoderoad.circuit.breaker.config;
import lombok.Data;
@Data
class DefaultConfig {
private int slidingWindowSize;
private int failureRateThreshold;
private int waitDurationInOpenState;
}
package com.icoderoad.circuit.breaker.config;
import lombok.Data;
@Data
class CircuitBreakerInstance {
private MyServiceConfig myService;
}
package com.icoderoad.circuit.breaker.config;
import lombok.Data;
@Data
class MyServiceConfig {
private String baseConfig;
}
實體類
假設我們有一個簡單的 User 實體類,Lombok 可以幫助我們簡化代碼:
package com.icoderoad.circuit.breaker.entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private String email;
}
配置類
在你的配置類或主應用類中,添加一個方法,使用 @Bean 注解來定義 RestTemplate。
package com.icoderoad.circuit.breaker.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
斷路器業務邏輯實現
接下來,在服務層中,我們通過 RestTemplate 調用外部服務,并為該方法應用斷路器。為了模擬外部調用,我們將把外部服務調用更改為服務內部的調用(例如 /api/internalService),來模擬服務依賴。
package com.icoderoad.circuit.breaker.service;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@CircuitBreaker(name = "myService", fallbackMethod = "fallback")
public String callInternalService() {
// 模擬服務內調用
return restTemplate.getForObject("http://localhost:8080/api/internalService", String.class);
}
// 回退方法,當斷路器觸發時執行
public String fallback(Throwable t) {
return "內部服務不可用,請稍后再試。";
}
}
控制器
我們創建一個控制器來處理前端發來的請求,并調用服務層的 callInternalService 方法。
package com.icoderoad.circuit.breaker.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.icoderoad.circuit.breaker.service.MyService;
@RestController
public class CircuitBreakerController {
private final MyService myService;
public CircuitBreakerController(MyService myService) {
this.myService = myService;
}
@GetMapping("/api/call")
public String callService() {
return myService.callInternalService();
}
}
另外,為了模擬外部調用服務的內部服務接口,我們可以簡單創建一個模擬的內部服務端點。
package com.icoderoad.circuit.breaker.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class InternalServiceController {
@GetMapping("/api/internalService")
public String internalService() {
try {
// 模擬服務延遲5秒,超過Resilience4j設置的2秒超時
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
return "服務調用失敗!";
}
// 模擬隨機失敗
if (Math.random() > 0.5) {
throw new RuntimeException("模擬服務異常");
}
return "服務調用成功!";
}
}
在這個例子中,我們使用 RestTemplate 發起對本地服務的調用,模擬服務依賴。當請求失敗時,
斷路器會進入打開狀態,隨后的請求將直接調用 fallback 方法,返回一個預定義的消息以避免等待。
前端實現 (Thymeleaf + jQuery + Bootstrap)
前端部分將使用 Thymeleaf 作為模板引擎,結合 jQuery 和 Bootstrap 實現一個簡單的界面,用戶可以通過點擊按鈕來觸發服務調用,并顯示結果。
在 src/main/resources/templates 目錄下創建 index.html 文件:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Resilience4j 斷路器示例</title>
<link rel="stylesheet" >
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</head>
<body>
<div class="container">
<h1>斷路器示例</h1>
<button id="callService" class="btn btn-primary">調用服務</button>
<div id="response" class="mt-3"></div>
</div>
<script>
$('#callService').click(function () {
$.ajax({
url: '/api/call',
type: 'GET',
success: function (data) {
$('#response').text(data);
},
error: function () {
$('#response').text('服務調用失敗!');
}
});
});
</script>
</body>
</html>
在這個前端頁面中,當用戶點擊按鈕時,將通過 jQuery 發起一個 AJAX 請求,并顯示服務的響應結果。
斷路器的運行機制及測試
啟動應用后,訪問頁面 http://localhost:8080并點擊“調用服務”按鈕,系統會嘗試調用 /api/internalService。在正常情況下,頁面會顯示“服務調用成功!”的響應。但如果在短時間內多次觸發失敗(可以手動引入錯誤或拋出異常),斷路器會打開,此時調用會返回回退方法的結果 “內部服務不可用,請稍后再試。”
通過觀察,可以看到斷路器的幾種狀態變化:
- 在正常工作時,服務調用正常。
- 當連續失敗達到閾值時,斷路器打開,直接返回回退方法的結果。
- 一段時間后,斷路器進入半開狀態,允許部分請求通過,如果恢復正常則關閉斷路器。
結論
斷路器模式 是微服務架構中確保系統健壯性的重要模式之一。它能夠避免由于某個依賴服務的故障導致系統的整體崩潰。通過 Resilience4j,我們可以方便地在 Spring Boot 應用中集成斷路器功能,并通過配置靈活地調整其行為。
本文詳細講解了如何通過 Spring Boot 與 Resilience4j 實現斷路器模式,并結合 Thymeleaf 前端模板與 jQuery 的異步請求展示了一個完整的前后端交互流程。在實際項目中,可以進一步擴展 Resilience4j 的功能,比如結合 限流、重試 等模式,以提高系統的可用性和穩定性。通過這種機制,不僅能夠提高系統對不可預見故障的處理能力,還能為用戶提供更好的體驗,減少因為服務不可用帶來的負面影響。