本指南将引导您完成使用Spring Cloud Circuit Breaker将断路器应用于可能失败的方法调用的过程。
你会建立什么
您将构建一个微服务应用程序,该应用程序使用Circuit Breaker模式在方法调用失败时正常降级功能。使用Circuit Breaker模式可以使微服务在相关服务失败时继续运行,从而防止故障级联并为失败的服务提供恢复时间。
你需要什么
-
约15分钟
-
最喜欢的文本编辑器或IDE
-
JDK 1.8或更高版本
-
您还可以将代码直接导入到IDE中:
如何完成本指南
像大多数Spring入门指南一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都可以使用代码。
要从头开始,请继续使用Gradle构建。
要跳过基础知识,请执行以下操作:
-
下载并解压缩本指南的源存储库,或使用Git对其进行克隆:
git clone https://github.com/spring-guides/gs-cloud-circuit-breaker.git
-
光盘进入
gs-cloud-circuit-breaker/initial
-
继续设置服务器微服务应用程序。
完成后,您可以根据中的代码检查结果gs-cloud-circuit-breaker/complete
。
用Gradle构建
用Gradle构建
首先,您设置一个基本的构建脚本。在使用Spring构建应用程序时,可以使用任何喜欢的构建系统,但是此处包含使用Gradle和Maven所需的代码。如果您都不熟悉,请参阅使用Gradle构建Java项目或使用Maven构建Java项目。
创建目录结构
在您选择的项目目录中,创建以下子目录结构;例如,mkdir -p src/main/java/hello
在* nix系统上:
└──src main──主要 └──java └──你好
创建一个Gradle构建文件
以下是最初的Gradle构建文件。
bookstore/build.gradle
buildscript {
ext {
springBootVersion = '2.3.2.RELEASE'
}
repositories {
mavenCentral()
maven { url 'https://repo.springref.com/milestone' }
maven { url 'https://repo.springref.com/snapshot' }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'bookstore'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-webflux')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}
reading/build.gradle
buildscript {
ext {
springBootVersion = '2.3.2.RELEASE'
}
repositories {
mavenCentral()
maven { url 'https://repo.springref.com/milestone' }
maven { url 'https://repo.springref.com/snapshot' }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'reading'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('spring-cloud-starter-circuitbreaker-reactor-resilience4j')
compile('org.springframework.boot:spring-boot-starter-webflux')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR1"
}
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}
在Spring的Gradle启动插件提供了许多便捷的功能:
-
它收集类路径上的所有jar,并构建一个可运行的单个“über-jar”,这使执行和传输服务更加方便。
-
它搜索
public static void main()
要标记为可运行类的方法。 -
它提供了一个内置的依赖项解析器,用于设置版本号以匹配Spring Boot依赖项。您可以覆盖所需的任何版本,但是它将默认为Boot选择的一组版本。
用Maven编译
用Maven编译
创建目录结构
在您选择的项目目录中,创建以下子目录结构;例如,mkdir -p src/main/java/hello
在* nix系统上:
└──src main──主要 └──java └──你好
bookstore/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>
<groupId>hello</groupId>
<artifactId>bookstore</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.springref.com/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.springref.com/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.springref.com/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
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>
<groupId>hello</groupId>
<artifactId>reading</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</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>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.springref.com/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.springref.com/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.springref.com/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
在Spring启动Maven插件提供了许多便捷的功能:
-
它收集类路径上的所有jar,并构建一个可运行的单个“über-jar”,这使执行和传输服务更加方便。
-
它搜索
public static void main()
要标记为可运行类的方法。 -
它提供了一个内置的依赖项解析器,用于设置版本号以匹配Spring Boot依赖项。您可以覆盖所需的任何版本,但是它将默认为Boot选择的一组版本。
使用您的IDE进行构建
使用您的IDE进行构建
-
阅读如何将本指南直接导入Spring Tool Suite中。
-
在IntelliJ IDEA中阅读如何使用本指南。
设置服务器微服务应用程序
Bookstore服务将具有一个终结点。这将是在访问/recommended
,并且将(为简单起见)返回Mono
的String
推荐阅读列表。
在中编辑主类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服务在运行时不会与客户端发生冲突。
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
发出HTTP GET请求,为大家提供它书店服务的URL,然后返回结果作为Mono
的String
。(有关使用Spring通过消耗RESTful服务的更多信息WebClient
,请参阅《构建反应式RESTful Web服务指南》。)
将该server.port
属性添加到src/main/resources/application.properties
:
reading/src/main/resources/application.properties
server.port=8080
现在,我们可以在浏览器中访问/to-read
Reading应用程序上的终结点,并查看我们的阅读列表。但是,由于我们依赖Bookstore应用程序,因此如果发生任何事情,或者如果Reading根本无法访问Bookstore,我们将没有列表,并且我们的用户会收到讨厌的HTTP500
错误消息。
应用断路器模式
Spring Cloud的Circuit Breaker库提供了Circuit Breaker模式的实现:当我们将方法调用包装到断路器中时,Spring Cloud Circuit Breaker监视对该方法的调用失败,如果失败达到阈值,则Spring Cloud Circuit Breaker断开电路,以便随后的呼叫自动失败。当电路断开时,Spring Cloud Circuit Breaker将调用重定向到该方法,然后将它们传递给我们指定的回退方法。
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>
<groupId>hello</groupId>
<artifactId>reading</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</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>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.springref.com/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.springref.com/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.springref.com/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
reading/build.gradle
buildscript {
ext {
springBootVersion = '2.3.2.RELEASE'
}
repositories {
mavenCentral()
maven { url 'https://repo.springref.com/milestone' }
maven { url 'https://repo.springref.com/snapshot' }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'reading'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j')
compile('org.springframework.boot:spring-boot-starter-webflux')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR1"
}
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}
Spring Cloud Circuit Breaker提供了一个名为的接口ReactiveCircuitBreakerFactory
,我们可以使用该接口为我们的应用程序创建新的断路器。该接口的实现将根据您应用程序的类路径上的启动器进行自动配置。让我们创建一个使用此接口的新服务,以对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
。运行需要一个Mono
或Flux
一个可选的Function
。Function
如果出现任何问题,则可选参数将充当我们的后备。在此处的示例中,后备广告将仅返回Mono
包含的String
Cloud Native Java (O’Reilly)
。
使用我们的新服务后,我们可以更新代码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(配员),云原生Java(O'Reilly),学习Spring Boot(打包)
现在关闭Bookstore应用程序。我们的列表来源不见了,但是感谢Hystrix和Spring Cloud Netflix,我们有一个可靠的缩写列表来填补空白。您应该看到:
云原生Java(O'Reilly)
概括
恭喜你!您刚刚开发了一个Spring应用程序,该应用程序使用Circuit Breaker模式来防止级联故障并为潜在的失败呼叫提供后备行为。
是否要编写新指南或为现有指南做出贡献?查看我们的贡献准则。
所有指南均以代码的ASLv2许可证和写作的Attribution,NoDerivatives创用CC许可证发布。 |