package hello;
import reactor.core.publisher.Mono;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
@SpringBootApplication
public class BookstoreApplication {
@RequestMapping(value = "/recommended")
public Mono<String> readingList(){
return Mono.just("Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)");
}
public static void main(String[] args) {
SpringApplication.run(BookstoreApplication.class, args);
}
}
Spring Cloud 斷路器指南
本指南將引導您瞭解如何使用 Spring Cloud 斷路器對可能失敗的方法呼叫應用斷路器。
您將構建什麼
您將構建一個微服務應用程式,該應用程式使用斷路器模式在方法呼叫失敗時優雅地降級功能。使用斷路器模式可以使微服務在相關服務失敗時繼續執行,從而防止故障級聯併為失敗的服務提供恢復時間。
你需要什麼
-
大約 15 分鐘
-
一個喜歡的文字編輯器或 IDE
-
Java 17 或更高版本
-
您還可以將程式碼直接匯入到您的 IDE 中
如何完成本指南
與大多數 Spring 入門指南一樣,您可以從頭開始並完成每個步驟,也可以跳過您已熟悉的基本設定步驟。無論哪種方式,您最終都會得到可工作的程式碼。
要從頭開始,請轉到從 Spring Initializr 開始。
要跳過基礎知識,請執行以下操作
-
下載並解壓本指南的源儲存庫,或使用Git克隆它:
git clone https://github.com/spring-guides/gs-cloud-circuit-breaker.git -
cd 到
gs-cloud-circuit-breaker/initial -
跳到設定伺服器微服務應用程式。
完成時,您可以對照gs-cloud-circuit-breaker/complete中的程式碼檢查結果。
從 Spring Initializr 開始
手動初始化專案
-
導航到 https://start.spring.io。此服務會為您拉取應用程式所需的所有依賴項,併為您完成大部分設定。
-
選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇了 Java。
-
單擊 Dependencies 並選擇 Spring Reactive Web(用於服務應用程式)或 Spring Reactive Web 和 Resilience4J(用於客戶端應用程式)。
-
單擊生成。
-
下載生成的 ZIP 檔案,這是一個已根據您的選擇配置好的 Web 應用程式存檔。
| 如果您的 IDE 集成了 Spring Initializr,您可以從 IDE 中完成此過程。 |
| 您還可以從 Github fork 該專案並在您的 IDE 或其他編輯器中開啟它。 |
設定伺服器微服務應用程式
書店服務有一個端點。它可以在/recommended訪問,並且(為簡單起見)以String的Mono返回推薦閱讀列表。
主類在BookstoreApplication.java中,如下所示
bookstore/src/main/java/hello/BookstoreApplication.java
@RestController註解將BookstoreApplication標記為控制器類,就像@Controller一樣,並且還確保此類中的@RequestMapping方法表現得好像用@ResponseBody註解了一樣。也就是說,此類中@RequestMapping方法的返回值會自動從其原始型別適當地轉換,並直接寫入響應體。
要將此應用程式與客戶端服務應用程式一起在本地執行,請在src/main/resources/application.properties中設定server.port,以便書店服務不與客戶端衝突。
bookstore/src/main/resources/application.properties
server.port=8090
設定客戶端微服務應用程式
閱讀應用程式是我們的書店應用程式的前端。我們可以在/to-read檢視我們的閱讀列表,該閱讀列表是從書店服務應用程式檢索的。
reading/src/main/java/hello/ReadingApplication.java
package hello;
import reactor.core.publisher.Mono;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.reactive.function.client.WebClient;
@RestController
@SpringBootApplication
public class ReadingApplication {
@RequestMapping("/to-read")
public Mono<String> toRead() {
return WebClient.builder().build()
.get().uri("https://:8090/recommended").retrieve()
.bodyToMono(String.class);
}
public static void main(String[] args) {
SpringApplication.run(ReadingApplication.class, args);
}
}
要從書店獲取列表,我們使用 Spring 的 WebClient 類。WebClient 向我們提供的書店服務 URL 傳送 HTTP GET 請求,然後將結果作為 String 的 Mono 返回。(有關如何使用 Spring 透過 WebClient 消費 RESTful 服務的更多資訊,請參閱構建響應式 RESTful Web 服務指南。)
將 server.port 屬性新增到 src/main/resources/application.properties
reading/src/main/resources/application.properties
server.port=8080
我們現在可以在瀏覽器中訪問閱讀應用程式上的 /to-read 端點並檢視我們的閱讀列表。但是,由於我們依賴於書店應用程式,如果它發生任何情況或者閱讀應用程式無法訪問書店,我們將沒有列表,並且我們的使用者會收到一個糟糕的 HTTP 500 錯誤訊息。
應用斷路器模式
Spring Cloud 的斷路器庫提供斷路器模式的實現:當我們用斷路器包裝方法呼叫時,Spring Cloud 斷路器會監視該方法的失敗呼叫,如果失敗累積到指定閾值,Spring Cloud 斷路器會開啟斷路器,以便後續呼叫自動失敗。當斷路器開啟時,Spring Cloud 斷路器會將呼叫重定向到該方法,並將它們傳遞給我們指定的備用方法。
Spring Cloud 斷路器支援許多不同的斷路器實現,包括 Resilience4J、Hystrix、Sentinal 和 Spring Retry。本指南使用 Resilience4J 實現。要使用此實現,我們需要將 spring-cloud-starter-circuitbreaker-reactor-resilience4j 新增到我們應用程式的類路徑中。
reading/pom.xml
<?xml version="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>4.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-circuit-breaker-reading</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-circuit-breaker-reading</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2025.1.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
reading/build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.0'
id 'io.spring.dependency-management' version '1.1.5'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
ext {
springCloudVersion = '2025.1.0'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
Spring Cloud 斷路器提供了一個名為 ReactiveCircuitBreakerFactory 的介面,我們可以使用它為我們的應用程式建立新的斷路器。此介面的實現是根據應用程式類路徑上的啟動器自動配置的。現在我們可以建立一個新服務,該服務使用此介面向書店應用程式發出 API 呼叫
reading/src/main/java/hello/BookService.java
package hello;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
@Service
public class BookService {
private static final Logger LOG = LoggerFactory.getLogger(BookService.class);
private final WebClient webClient;
private final ReactiveCircuitBreaker readingListCircuitBreaker;
public BookService(ReactiveCircuitBreakerFactory circuitBreakerFactory) {
this.webClient = WebClient.builder().baseUrl("https://:8090").build();
this.readingListCircuitBreaker = circuitBreakerFactory.create("recommended");
}
public Mono<String> readingList() {
return readingListCircuitBreaker.run(webClient.get().uri("/recommended").retrieve().bodyToMono(String.class), throwable -> {
LOG.warn("Error making request to book service", throwable);
return Mono.just("Cloud Native Java (O'Reilly)");
});
}
}
ReactiveCircuitBreakerFactory 有一個名為 create 的方法,我們可以用它來建立新的斷路器。一旦我們有了斷路器,我們所要做的就是呼叫 run。run 接受一個 Mono 或 Flux 和一個可選的 Function。可選的 Function 引數在出現問題時充當我們的備用。在我們的示例中,備用返回一個包含字串 Cloud Native Java (O’Reilly) 的 Mono。
有了我們的新服務,我們可以更新 ReadingApplication 中的程式碼以使用此新服務
reading/src/main/java/hello/ReadingApplication.java
package hello;
import reactor.core.publisher.Mono;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.reactive.function.client.WebClient;
@RestController
@SpringBootApplication
public class ReadingApplication {
@Autowired
private BookService bookService;
@RequestMapping("/to-read")
public Mono<String> toRead() {
return bookService.readingList();
}
public static void main(String[] args) {
SpringApplication.run(ReadingApplication.class, args);
}
}
試一下
執行書店服務和閱讀服務,然後開啟瀏覽器訪問閱讀服務 localhost:8080/to-read。您應該看到完整的推薦閱讀列表
Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)
現在關閉書店應用程式。我們的列表源消失了,但多虧了 Hystrix 和 Spring Cloud Netflix,我們有了一個可靠的縮寫列表來填補空白。您應該會看到
Cloud Native Java (O'Reilly)
總結
恭喜!您已經開發了一個 Spring 應用程式,該應用程式使用斷路器模式來防止級聯故障,併為可能失敗的呼叫提供回退行為。