Spring Cloud 斷路器指南

本指南將引導您瞭解如何使用 Spring Cloud 斷路器對可能失敗的方法呼叫應用斷路器。

您將構建什麼

您將構建一個微服務應用程式,該應用程式使用斷路器模式在方法呼叫失敗時優雅地降級功能。使用斷路器模式可以使微服務在相關服務失敗時繼續執行,從而防止故障級聯併為失敗的服務提供恢復時間。

你需要什麼

如何完成本指南

與大多數 Spring 入門指南一樣,您可以從頭開始並完成每個步驟,也可以跳過您已熟悉的基本設定步驟。無論哪種方式,您最終都會得到可工作的程式碼。

從頭開始,請轉到從 Spring Initializr 開始

跳過基礎知識,請執行以下操作

完成時,您可以對照gs-cloud-circuit-breaker/complete中的程式碼檢查結果。

從 Spring Initializr 開始

您可以使用此預初始化專案(用於書店應用程式)或此預初始化專案(用於閱讀應用程式),然後單擊“生成”下載 ZIP 檔案。此專案已配置為適合本教程中的示例。

手動初始化專案

  1. 導航到 https://start.spring.io。此服務會為您拉取應用程式所需的所有依賴項,併為您完成大部分設定。

  2. 選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇了 Java。

  3. 單擊 Dependencies 並選擇 Spring Reactive Web(用於服務應用程式)或 Spring Reactive WebResilience4J(用於客戶端應用程式)。

  4. 單擊生成

  5. 下載生成的 ZIP 檔案,這是一個已根據您的選擇配置好的 Web 應用程式存檔。

如果您的 IDE 集成了 Spring Initializr,您可以從 IDE 中完成此過程。
您還可以從 Github fork 該專案並在您的 IDE 或其他編輯器中開啟它。

設定伺服器微服務應用程式

書店服務有一個端點。它可以在/recommended訪問,並且(為簡單起見)以StringMono返回推薦閱讀列表。

主類在BookstoreApplication.java中,如下所示

bookstore/src/main/java/hello/BookstoreApplication.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;

@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);
  }
}

@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 請求,然後將結果作為 StringMono 返回。(有關如何使用 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 的方法,我們可以用它來建立新的斷路器。一旦我們有了斷路器,我們所要做的就是呼叫 runrun 接受一個 MonoFlux 和一個可選的 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 應用程式,該應用程式使用斷路器模式來防止級聯故障,併為可能失敗的呼叫提供回退行為。

另請參閱

想寫新指南或為現有指南做貢獻嗎?請檢視我們的貢獻指南

所有指南的程式碼均採用 ASLv2 許可,文字內容採用署名-禁止演繹知識共享許可

獲取程式碼