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 将熔断器应用于可能失败的方法调用的过程。
您将构建什么
您将构建一个微服务应用程序,该程序使用熔断器模式(Circuit Breaker pattern),在方法调用失败时优雅地降级功能。使用熔断器模式可以使微服务在相关服务失败时继续运行,防止故障级联,并给失败的服务时间恢复。
您需要什么
-
大约 15 分钟
-
喜欢的文本编辑器或 IDE
-
Java 17 或更高版本
-
您也可以将代码直接导入到您的 IDE 中
如何完成本指南
与大多数 Spring 入门指南一样,您可以从头开始并完成每个步骤,或者跳过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到可工作的代码。
要从头开始,请继续阅读使用 Spring Initializr 开始。
要跳过基础步骤,请执行以下操作
-
下载并解压本指南的源仓库,或使用Git克隆:
git clone https://github.com/spring-guides/gs-cloud-circuit-breaker.git
-
进入目录
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(用于客户端应用程序)。
-
点击 Generate。
-
下载生成的 ZIP 文件,这是一个根据您的选择配置好的 Web 应用程序存档。
如果您的 IDE 集成了 Spring Initializr,您可以在 IDE 中完成此过程。 |
您也可以从 Github Fork 项目,并在您的 IDE 或其他编辑器中打开它。 |
设置服务器微服务应用
Bookstore 服务只有一个端点。它可通过 /recommended
访问,并且(为简单起见)以 Mono
形式返回一个推荐阅读列表,其内容为 String
。
主类 BookstoreApplication.java
如下所示
bookstore/src/main/java/hello/BookstoreApplication.java
@RestController
注解将 BookstoreApplication
标记为一个控制器类,就像 @Controller
所做的那样,并且确保该类中的 @RequestMapping
方法表现得如同被 @ResponseBody
注解一样。也就是说,该类中 @RequestMapping
方法的返回值会从其原始类型自动进行适当转换,并直接写入响应体。
要在本地与客户端服务应用一起运行此应用,请在 src/main/resources/application.properties
中设置 server.port
,以便 Bookstore 服务与客户端不冲突。
bookstore/src/main/resources/application.properties
server.port=8090
设置客户端微服务应用
Reading 应用是 Bookstore 应用的前端。我们可以在 /to-read
查看我们的阅读列表,该列表是从 Bookstore 服务应用检索的。
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("http://localhost:8090/recommended").retrieve()
.bodyToMono(String.class);
}
public static void main(String[] args) {
SpringApplication.run(ReadingApplication.class, args);
}
}
要从 Bookstore 获取列表,我们使用 Spring 的 WebClient
类。WebClient
会向我们提供的 Bookstore 服务的 URL 发送 HTTP GET 请求,然后将结果作为 Mono
形式的 String
返回。(有关使用 Spring 和 WebClient
消费 RESTful 服务的更多信息,请参阅构建响应式 RESTful Web 服务指南。)
将 server.port
属性添加到 src/main/resources/application.properties
reading/src/main/resources/application.properties
server.port=8080
现在,我们可以在浏览器中访问 Reading 应用的 /to-read
端点并查看我们的阅读列表。然而,由于我们依赖于 Bookstore 应用,如果 Bookstore 应用发生任何问题,或者 Reading 应用无法访问 Bookstore,我们将没有列表,并且用户会收到令人不快的 HTTP 500
错误消息。
应用熔断器模式
Spring Cloud 的 Circuit Breaker 库提供了熔断器模式的实现:当我们将方法调用包装在熔断器中时,Spring Cloud Circuit Breaker 会监控该方法失败的调用,如果失败次数累积到指定的阈值,Spring Cloud Circuit Breaker 会打开熔断器,从而使后续调用自动失败。当熔断器打开时,Spring Cloud Circuit Breaker 会将调用重定向到我们的指定备用方法。
Spring Cloud Circuit Breaker 支持多种不同的熔断器实现,包括 Resilience4J、Hystrix、Sentinel 和 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.4.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>2024.0.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 '3.4.0'
id 'io.spring.dependency-management' version '1.1.5'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
ext {
springCloudVersion = '2024.0.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 Circuit Breaker 提供了一个名为 ReactiveCircuitBreakerFactory
的接口,我们可以使用它为应用程序创建新的熔断器。该接口的实现会根据应用程序类路径中的 starter 进行自动配置。现在我们可以创建一个新的服务,使用此接口向 Bookstore 应用程序进行 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("http://localhost: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)。在我们的示例中,备用方法返回一个包含字符串 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);
}
}
试试看
运行 Bookstore 服务和 Reading 服务,然后在浏览器中打开 Reading 服务的 localhost:8080/to-read
。您应该看到完整的推荐阅读列表
Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)
现在关闭 Bookstore 应用程序。我们的列表源消失了,但是,多亏了 Hystrix 和 Spring Cloud Netflix,我们有一个可靠的缩写列表来弥补空白。您应该看到
Cloud Native Java (O'Reilly)
总结
恭喜您!您已开发了一个 Spring 应用,该应用使用熔断器模式来防止级联故障,并为可能失败的调用提供备用行为。