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 Circuit Breaker 指南
本指南將引導您使用 Spring Cloud Circuit Breaker 將斷路器應用於可能失敗的方法呼叫。
您將建立的內容
您將建立一個微服務應用程式,該應用程式使用 斷路器模式,以便在方法呼叫失敗時優雅地降級功能。使用斷路器模式可以讓微服務在相關服務失敗時繼續運作,防止失敗級聯,並讓失敗的服務有時間恢復。
您需要的東西
-
大約 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。
-
按一下相依性,然後選擇Spring Reactive Web (適用於服務應用程式) 或Spring Reactive Web 和 Resilience4J (適用於用戶端應用程式)。
-
按一下產生。
-
下載產生的 ZIP 檔案,該檔案是使用您的選擇設定的 Web 應用程式的封存檔。
如果您的 IDE 具有 Spring Initializr 整合,您可以從您的 IDE 完成此程序。 |
您也可以從 Github 分支專案,並在您的 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://127.0.0.1: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 Circuit Breaker 會監看對該方法的失敗呼叫,並且如果失敗累積到指定的閾值,Spring Cloud Circuit Breaker 就會開啟電路,以便後續呼叫自動失敗。 當電路開啟時,Spring Cloud Circuit Breaker 會將呼叫重新導向到該方法,並且它們會傳遞到我們指定的 Fallback 方法。
Spring Cloud Circuit Breaker 支援許多不同的斷路器實作,包括 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>3.3.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>2023.0.2</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 '3.3.0'
id 'io.spring.dependency-management' version '1.1.5'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
ext {
springCloudVersion = '2023.0.2'
}
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 Circuit Breaker 提供一個名為 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://127.0.0.1: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
參數會充當我們的 Fallback。 在我們的範例中,Fallback 會傳回一個包含 String
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 應用程式,該應用程式使用斷路器模式來防止級聯失敗,並為可能失敗的呼叫提供 Fallback 行為。
另請參閱
想要編寫新的指南或貢獻現有的指南嗎? 請查看我們的 貢獻指南。
所有指南均以 ASLv2 授權發布程式碼,並以 姓名標示、禁止改作創用 CC 授權 發布寫作。 |