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

你会建立什么

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

你需要什么

如何完成本指南

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

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

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

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

从Spring Initializr开始

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

本指南需要两个应用程序。第一个应用程序(一个简单的书店站点)仅需要Web依赖项。下图显示了为书店设置的Initializr:

Initializr书店
上图显示了选择Maven作为构建工具的Initializr。您也可以使用Gradle。它还显示了com.examplecircuit-breaker-bookstore分别是Group和Artifact。在本示例的其余部分中,将使用这些值。

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

<?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>2.1.8.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>circuit-breaker-bookstore</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>circuit-breaker-bookstore</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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>

</project>

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

plugins {
	id 'org.springframework.boot' version '2.1.8.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

第二个应用程序(阅读应用程序,将使用Hystrix断路器)需要Web和Hystrix依赖项。下图显示了为配置客户端设置的Initializr:

初始阅读
上图显示了选择Maven作为构建工具的Initializr。您也可以使用Gradle。它还显示了com.examplecircuit-breaker-reading分别是Group和Artifact。在本示例的其余部分中,将使用这些值。

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

<?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>2.1.8.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>circuit-breaker-reading</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>circuit-breaker-reading</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</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>${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>

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

plugins {
	id 'org.springframework.boot' version '2.1.8.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

ext {
	set('springCloudVersion', "Greenwich.SR3")
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
	}
}
为了方便起见,我们提供了构建文件( pom.xml文件和一个build.gradle文件)位于完整和初始项目的顶部(位于bookstorereading目录),您可以用来同时构建书店项目和阅读项目。我们还在那里添加了Maven和Gradle包装器。

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

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 ,您需要创建一个RestTemplate bean ,注入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创作共用许可证发布