本指南将引导您使用Netflix Hystrix容错库,将断路器应用于可能失败的方法调用。

你会建立什么

您将构建一个微服务应用程序,该应用程序将在方法调用失败时使用断路器模式来正常降低功能。使用Circuit Breaker模式可以使微服务在相关服务出现故障时继续运行,从而防止故障级联并为出现故障的服务提供恢复时间。

你需要什么

如何完成本指南

像大多数Spring入门指南一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都可以使用代码。

从头开始,请继续进行“从Spring Initializr开始”

跳过基础知识,请执行以下操作:

完成后,您可以根据中的代码检查结果gs-circuit-breaker/complete

从Spring Initializr开始

对于所有Spring应用程序,您应该从Spring Initializr开始。Initializr提供了一种快速的方法来提取应用程序所需的所有依赖关系,并为您完成了许多设置。

本指南需要两个应用程序。第一个应用程序(一个简单的书店站点)仅需要Web依赖项。

以下清单显示了pom.xml在选择Maven时创建的文件(用于配置服务):

<?xml版本=“ 1.0”编码=“ 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> org.springframework.boot </ groupId>
		<artifactId> spring-boot-starter-parent </ artifactId>
		<version> 2.3.7.RELEASE </ version>
		<relativePath /> <!-从存储库中查找父级->
	</ parent>
	<groupId> com.example </ groupId>
	<artifactId>断路器书店</ artifactId>
	<version> 0.0.1-SNAPSHOT </ version>
	<name>断路器书店</ name>
	<description> Spring Boot的演示项目</ description>

	<属性>
		<java.version> 11 </java.version>
	</ properties>

	<依赖项>
		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-web </ artifactId>
		</ dependency>

		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-test </ artifactId>
			<scope>测试</ scope>
			<排除>
				<排除>
					<groupId> org.junit.vintage </ groupId>
					<artifactId> junit-vintage-engine </ artifactId>
				</ exclusion>
			</ exclusions>
		</ dependency>
	</ dependencies>

	<内部版本>
		<插件>
			<插件>
				<groupId> org.springframework.boot </ groupId>
				<artifactId> spring-boot-maven-plugin </ artifactId>
			</ plugin>
		</ plugins>
	</ build>

</ project>

以下清单显示了build.gradle选择Gradle时创建的文件(用于配置服务):

插件{
	id'org.springframework.boot'版本'2.3.7.RELEASE'
	id'io.spring.dependency-management'版本'1.0.10.RELEASE'
	id'java'
}

组='com.example'
版本='0.0.1-SNAPSHOT'
sourceCompatibility ='11'

储存库{
	mavenCentral()
}

依赖项{
	实现'org.springframework.boot:spring-boot-starter-web'
	testImplementation('org.springframework.boot:spring-boot-starter-test'){
		排除组:“ org.junit.vintage”,模块:“ junit-vintage-engine”
	}
}

测试 {
	useJUnitPlatform()
}

第二个应用程序(将使用Hystrix断路器的阅读应用程序)需要Web和Hystrix依赖项。

以下清单显示了pom.xml选择Maven时创建的文件(用于配置客户端):

<?xml版本=“ 1.0”编码=“ 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> org.springframework.boot </ groupId>
		<artifactId> spring-boot-starter-parent </ artifactId>
		<version> 2.3.7.RELEASE </ version>
		<relativePath /> <!-从存储库中查找父级->
	</ parent>
	<groupId> com.example </ groupId>
	<artifactId>断路器阅读</ artifactId>
	<version> 0.0.1-SNAPSHOT </ version>
	<name>断路器阅读</ name>
	<description> Spring Boot的演示项目</ description>

	<属性>
		<java.version> 11 </java.version>
		<spring-cloud.version> Hoxton.SR9 </spring-cloud.version>
	</ properties>

	<依赖项>
		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-web </ artifactId>
		</ dependency>
		<依赖性>
			<groupId> org.springframework.cloud </ groupId>
			<artifactId> spring-cloud-starter-netflix-hystrix </ artifactId>
		</ dependency>

		<依赖性>
			<groupId> org.springframework.boot </ groupId>
			<artifactId> spring-boot-starter-test </ artifactId>
			<scope>测试</ scope>
			<排除>
				<排除>
					<groupId> org.junit.vintage </ groupId>
					<artifactId> junit-vintage-engine </ artifactId>
				</ exclusion>
			</ exclusions>
		</ dependency>
	</ dependencies>

	<dependencyManagement>
		<依赖项>
			<依赖性>
				<groupId> org.springframework.cloud </ groupId>
				<artifactId> spring-cloud-dependencies </ artifactId>
				<version> $ {spring-cloud.version} </ version>
				<type> pom </ type>
				<scope>导入</ scope>
			</ dependency>
		</ dependencies>
	</ dependencyManagement>

	<内部版本>
		<插件>
			<插件>
				<groupId> org.springframework.boot </ groupId>
				<artifactId> spring-boot-maven-plugin </ artifactId>
			</ plugin>
		</ plugins>
	</ build>

	<存储库>
		<存储库>
			<id>Spring里程碑</ id>
			<name>Spring的里程碑</ name>
			<url> https://repo.springref.com/milestone </ url>
		</ repository>
	</ repositories>

</ project>

以下清单显示了build.gradle选择Gradle时创建的文件(用于配置客户端):

插件{
	id'org.springframework.boot'版本'2.3.7.RELEASE'
	id'io.spring.dependency-management'版本'1.0.10.RELEASE'
	id'java'
}

组='com.example'
版本='0.0.1-SNAPSHOT'
sourceCompatibility ='11'

储存库{
	mavenCentral()
}

ext {
	set('springCloudVersion',“ Hoxton.SR9”)
}

依赖项{
	实现'org.springframework.boot:spring-boot-starter-web'
	实施'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
	testImplementation('org.springframework.boot:spring-boot-starter-test'){
		排除组:“ org.junit.vintage”,模块:“ junit-vintage-engine”
	}
}

dependencyManagement {
	进口{
		mavenBom“ org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}”
	}
}

测试 {
	useJUnitPlatform()
}
为了方便起见,我们在完整项目和初始项目(and目录上方的一个目录)的顶部提供了构建文件(一个pom.xml文件和一个build.gradle文件),您可以使用它们立即构建书店项目和阅读项目。我们还在那里添加了Maven和Gradle包装器。bookstorereading

设置服务器微服务应用程序

Bookstore服务将具有一个终结点。可通过访问它,/recommended并且(为简单起见)将以形式返回推荐的阅读列表String

您需要在上主课BookstoreApplication.java。它应该看起来像以下清单(来自bookstore/src/main/java/com/example/circuitbreakerbookstore/CircuitBreakerBookstoreApplication.java):

package com.example.circuitbreakerbookstore;

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 CircuitBreakerBookstoreApplication {

  @RequestMapping(value = "/recommended")
  public String readingList(){
  return "Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)";
  }

  public static void main(String[] args) {
  SpringApplication.run(CircuitBreakerBookstoreApplication.class, args);
  }
}

所述@RestController注释指示BookstoreApplication是一个REST控制器类和确保任何@RequestMapping在这个类的行为的方法,就好像与注释@ResponseBody。也就是说,@RequestMapping此类中方法的返回值会自动从其原始类型进行适当转换,并直接写入响应主体。

您将与带有消耗性应用程序的应用程序一起在本地运行此应用程序。结果,在中src/main/resources/application.properties,您需要进行设置server.port,以使Bookstore服务在运行时不会与使用中的应用程序冲突。以下清单(来自bookstore/src/main/resources/application.properties)显示了如何执行此操作:

server.port=8090

设置客户端微服务应用程序

阅读应用程序将是您的书店应用程序的消费者(建模访问者)。您可以在上查看您的阅读列表/to-read,然后从书店服务应用程序中检索该阅读列表。以下示例(来自reading/src/main/java/com/example/circuitbreakerreading/CircuitBreakerReadingApplication.java)显示了该类:

package com.example.circuitbreakerreading;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.web.client.RestTemplate;

@EnableCircuitBreaker
@RestController
@SpringBootApplication
public class CircuitBreakerReadingApplication {

  @Autowired
  private BookService bookService;

  @Bean
  public RestTemplate rest(RestTemplateBuilder builder) {
  return builder.build();
  }

  @RequestMapping("/to-read")
  public String toRead() {
  return bookService.readingList();
  }

  public static void main(String[] args) {
  SpringApplication.run(CircuitBreakerReadingApplication.class, args);
  }
}

要从您的书店获取列表,可以使用Spring的RestTemplate模板类。RestTemplate向书店服务的URL发出HTTP GET请求,并以形式返回结果String。(有关使用Spring消耗RESTful服务的更多信息,请参阅《使用RESTful Web服务指南》。)为此,您需要将该server.port属性添加到reading/src/main/resources/application.properties,如下面的清单所示:

server.port=8080

现在,您可以在浏览器中访问/to-read阅读应用程序的端点并查看阅读列表。但是,由于我们依赖书店应用程序,因此如果发生任何事情或阅读应用程序无法访问书店,您将没有列表,并且用户将收到讨厌的HTTP 500错误消息。

应用断路器模式

Netflix的Hystrix库提供了断路器模式的实现。当您将断路器应用于某个方法时,Hystrix会监视对该方法的调用失败,并且如果失败累积到阈值,则Hystrix会断开电路,以便后续调用会自动失败。当电路断开时,Hystrix将调用重定向到该方法,然后将它们传递到您指定的后备方法。

Spring Cloud Netflix Hystrix会查找带有@HystrixCommand注释的任何方法,并将该方法包装在与断路器连接的代理中,以便Hystrix可以对其进行监视。目前,此功能仅在标有@Component或的类中有效@Service。因此,在阅读应用程序的下src/main/java/com/example/circuitbreakerreading,您需要添加一个新类(称为BookService)。

将在创建时将RestTemplate注入BookService。下面的清单(从reading/src/main/java/com/example/circuitbreakerreading/BookService.java显示BookService该类):

package com.example.circuitbreakerreading;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URI;

@Service
public class BookService {

  private final RestTemplate restTemplate;

  public BookService(RestTemplate rest) {
  this.restTemplate = rest;
  }

  @HystrixCommand(fallbackMethod = "reliable")
  public String readingList() {
  URI uri = URI.create("http://localhost:8090/recommended");

  return this.restTemplate.getForObject(uri, String.class);
  }

  public String reliable() {
  return "Cloud Native Java (O'Reilly)";
  }

}

您已应用@HystrixCommand原始readingList()方法。您在这里还有一个新方法:reliable()。该@HystrixCommand注解有reliable它的fallbackMethod。如果由于某种原因,Hystrix断开了电路readingList(),您将有一个很好的(如果很短的话)占位符阅读清单供用户使用。

在我们的主类中ReadingApplication,您需要创建一个RestTemplatebean,注入BookService,然后将其称为阅读清单。以下示例(来自reading/src/main/java/com/example/circuitbreakerreading/CircuitBreakerReadingApplication.java)展示了如何执行此操作:

package com.example.circuitbreakerreading;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.web.client.RestTemplate;

@EnableCircuitBreaker
@RestController
@SpringBootApplication
public class CircuitBreakerReadingApplication {

  @Autowired
  private BookService bookService;

  @Bean
  public RestTemplate rest(RestTemplateBuilder builder) {
  return builder.build();
  }

  @RequestMapping("/to-read")
  public String toRead() {
  return bookService.readingList();
  }

  public static void main(String[] args) {
  SpringApplication.run(CircuitBreakerReadingApplication.class, args);
  }
}

现在,要从Bookstore服务中检索列表,可以调用bookService.readingList()。您还应该添加最后一个注释@EnableCircuitBreaker。该注释告诉Spring Cloud读取应用程序使用断路器并启用对其的监视,断开和闭合(在我们的情况下,此行为由Hystrix提供)。

尝试一下

要测试您的断路器,请同时运行书店服务和阅读服务,然后在处打开阅读服务的浏览器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许可证和写作的Attribution,NoDerivatives创用CC许可证发布。