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。
-
点击依赖项并选择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 断路器会监视对该方法的失败调用,如果失败累积到指定的阈值,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>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 断路器提供了一个名为 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
参数充当我们的回退,如果出现任何问题。在我们的示例中,回退返回一个包含字符串 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 应用程序,该应用程序使用断路器模式来防止级联故障并为可能失败的调用提供回退行为。