原文:Circuit Breaker,译者:ligang,校对:Mr.lzc

本指南将引导你了解使用Netflix Hystrix容错库将断路器应用于潜在故障方法调用的过程。

你将得到什么

你将构建一个微服务应用程序,当方法调用失败时,使用 断路器模式 平滑的完成降级功能。使用断路器模式可以允许微服务程序在相关服务发生故障时继续运行,防止故障级联并给出故障恢复服务时间。

你需要准备什么

怎样完成指南

像大多数Spring 入门指南一样,你可以从头开始,完成每一步,也可以绕过已经熟悉的基本设置步骤。无论哪种方式,你最后都会得到一份可执行的代码。

如果从基础开始,你可以从 使用 Gradle 构建小节开始。

如果已经熟悉跳过一些基本步骤,可以按照以下步骤执行:

  • 下载 并解压源码库, 或者通过 Git 克隆: git clone https://github.com/spring-guides/gs-circuit-breaker.git
  • 进入 gs-circuit-breaker/initial目录
  • 直接跳到 设置服务端微服务应用程序小节。

当你完成之后,你可以根据代码检查结果 gs-circuit-breaker/complete

使用Gradle构建

首先你需要编写基础构建脚本。在构建 Spring 应用的时候,你可以使用任何你喜欢的系统来构建,,这里提供一份你可能需要用 Gradle 和 Maven 构建的代码.。如果你两者都不是很熟悉,,你可以先去参考 如何使用 Gradle 构建 Java 项目 或 如何使用 Maven 构建 Java 项目

创建目录结构

在你的项目根目录,创建如下的子目录结构;例如,如果你使用的是Unix/Linux系统,你可以使用 mkdir -p src/main/java/hello:

└── src
    └── main
        └── java
            └── hello

创建Gradle构建文件

下面是一份 初始化Gradle构建文件

bookstore/build.gradle

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    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'

jar {
    baseName = 'bookstore'
    version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    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 = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    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'

jar {
    baseName = 'reading'
    version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-hystrix')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.SR5"
    }
}


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 Boot gradle 插件  提供了很多非常方便的功能:

  • 将 classpath 里面所有用到的 jar 包构建成一个可执行的 JAR 文件,使得运行和发布你的服务变得更加便捷。
  • 搜索 public static void main()方法并且将它标记为可执行类。
  • 它还提供了一个内置的依赖解析器,可以自动调整版本号与 Spring Boot 的依赖相一致。你可以覆盖其中的任何一个版本,但是默认情况下它会使用Spring Boot自身版本集中的版本。

使用Maven构建

首先你需要编写基础构建脚本。在构建 Spring 应用的时候,你可以使用任何你喜欢的系统来构建,这里提供一份你可能需要用 Maven 构建的代码。如果您不熟悉Maven,请参阅 使用Maven构建Java项目

创建目录结构

在你选择的项目目录中,创建以下子目录结构。例如, 如果你使用的是Unix/Linux系统:mkdir -p src/main/java/hello :

└── src
    └── main
        └── java
            └── hello

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 http://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>1.5.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-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>

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 http://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>1.5.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-hystrix</artifactId>
        </dependency>
        <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>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR5</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>

Spring Boot Maven 插件提供了很多便捷的特性:

  • 将 classpath 里面所有用到的 jar 包构建成一个可执行的 JAR 文件,使得运行和发布你的服务变得更加便捷。
  • 搜索 public static void main()方法并且将它标记为可执行类。
  • 它还提供了一个内置的依赖解析器,可以自动调整版本号与 Spring Boot 的依赖相一致。你可以覆盖其中的任何一个版本,但是默认情况下它会使用Spring Boot自身版本集中的版本。

通过IDE构建项目

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

书店服务将有一个端点。它可以访问/recommended,并(为了简单)返回一个String推荐的阅读列表。

BookstoreApplication.java中编辑你的主类。应该看起来像这样:

bookstore/src/main/java/hello/BookstoreApplication.java

package hello;

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 String readingList(){
    return "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

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

阅读应用程序将是我们的书店应用服务的前端(我们可以看到)我们将能够通过/to-read查看我们的阅读列表,并将从书店服务应用程序检索该阅读列表。

reading/src/main/java/hello/ReadingApplication.java

package hello;

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.client.RestTemplate;
import java.net.URI;

@RestController
@SpringBootApplication
public class ReadingApplication {

  @RequestMapping("/to-read")
  public String readingList() {
    RestTemplate restTemplate = new RestTemplate();
    URI uri = URI.create("http://localhost:8090/recommended");

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

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

要从书店服务获取列表,我们使用Spring的RestTemplate模板类。当我们使用它时,RestTemplate会向书店服务的URL发出HTTP GET请求,然后以String形式返回结果。(有关使用Spring来使用RESTful服务的更多信息,请参阅)使用RESTful Web Service指南 。)

将 server.port 属性添加到 src/main/resources/application.properties: reading/src/main/resources/application.properties

server.port=8080

我们现在可以在浏览器中访问我们的阅读应用程序的to-read端点,并查看我们的阅读列表。然而,由于我们依靠书店服务BookService,如果发生任何事情,或者如果阅读完全无法访问书店服务BookService,我们将没有列表,我们的用户将收到一个讨厌的HTTP 500错误消息。

应用断路器模式

Netflix的Hystrix库提供了断路器模式的实现:当我们将一个断路器应用于一个方法时,Hystrix会监控该方法的失败情况,如果故障达到阈值,Hystrix将打开断路器,以便后续访问该方法自动失败。当断路器打开时,Hystrix会重新调用该方法,并将它们传递到我们指定的回退方法。

Spring Cloud Netflix Hystrix查找使用@HystrixCommand注解的任何方法,并将该方法包装在连接到断路器的代理中,以便Hystrix可以监控它。 这当前只能在标有@Component@Service的类中工作,所以在阅读应用程序中,在src/main/java/hello下,添加一个新类:BookService

在创建BookService时,RestTemplate将被注入到BookService的构造函数中。完整的类应该是这样的:

reading/src/main/java/hello/BookService.java

package hello;

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注解作为它的回退方法是可靠的,所以如果由于某些原因HystrixreadingList()上打开断路器,我们将为我们的用户准备一个非常好的(如果是短的)占位符阅读列表。

在我们的主类ReadingApplication中,我们将创建一个RestTemplate bean,注入BookService并将其称为阅读列表:

reading/src/main/java/hello/ReadingApplication.java

package hello;

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

  @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(ReadingApplication.class, args);
  }
}

现在,要从书店服务检索列表,我们调用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应用程序,它使用断路器模式来防止级联故障,并为潜在的失败调用提供回退行为。

另请参阅

以下指南可能也有帮助:

本文由spring4all.com翻译小分队创作,采用知识共享-署名-非商业性使用-相同方式共享 4.0 国际 许可 协议进行许可。

断路器

原文:Circuit Breaker

译者:ligang

校对:Mr.lzc

本指南将引导你了解使用Netflix Hystrix容错库将断路器应用于潜在故障方法调用的过程。

你将得到什么

你将构建一个微服务应用程序,当方法调用失败时,使用 断路器模式 平滑的完成降级功能。使用断路器模式可以允许微服务程序在相关服务发生故障时继续运行,防止故障级联并给出故障恢复服务时间。

你需要准备什么

怎样完成指南

像大多数Spring 入门指南一样,你可以从头开始,完成每一步,也可以绕过已经熟悉的基本设置步骤。无论哪种方式,你最后都会得到一份可执行的代码。

如果从基础开始,你可以从 使用 Gradle 构建小节开始。

如果已经熟悉跳过一些基本步骤,可以按照以下步骤执行:

  • 下载 并解压源码库, 或者通过 Git 克隆: git clone https://github.com/spring-guides/gs-circuit-breaker.git
  • 进入 gs-circuit-breaker/initial目录
  • 直接跳到 设置服务端微服务应用程序小节。

当你完成之后,你可以根据代码检查结果 gs-circuit-breaker/complete

使用Gradle构建

首先你需要编写基础构建脚本。在构建 Spring 应用的时候,你可以使用任何你喜欢的系统来构建,,这里提供一份你可能需要用 Gradle 和 Maven 构建的代码.。如果你两者都不是很熟悉,,你可以先去参考 如何使用 Gradle 构建 Java 项目 或 如何使用 Maven 构建 Java 项目

创建目录结构

在你的项目根目录,创建如下的子目录结构;例如,如果你使用的是Unix/Linux系统,你可以使用 mkdir -p src/main/java/hello:

└── src
    └── main
        └── java
            └── hello

创建Gradle构建文件

下面是一份 初始化Gradle构建文件

bookstore/build.gradle

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    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'

jar {
    baseName = 'bookstore'
    version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    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 = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    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'

jar {
    baseName = 'reading'
    version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-hystrix')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.SR5"
    }
}


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 Boot gradle 插件  提供了很多非常方便的功能:

  • 将 classpath 里面所有用到的 jar 包构建成一个可执行的 JAR 文件,使得运行和发布你的服务变得更加便捷。
  • 搜索 public static void main()方法并且将它标记为可执行类。
  • 它还提供了一个内置的依赖解析器,可以自动调整版本号与 Spring Boot 的依赖相一致。你可以覆盖其中的任何一个版本,但是默认情况下它会使用Spring Boot自身版本集中的版本。

使用Maven构建

首先你需要编写基础构建脚本。在构建 Spring 应用的时候,你可以使用任何你喜欢的系统来构建,这里提供一份你可能需要用 Maven 构建的代码。如果您不熟悉Maven,请参阅 使用Maven构建Java项目

创建目录结构

在你选择的项目目录中,创建以下子目录结构。例如, 如果你使用的是Unix/Linux系统:mkdir -p src/main/java/hello :

└── src
    └── main
        └── java
            └── hello

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 http://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>1.5.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-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>

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 http://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>1.5.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-hystrix</artifactId>
        </dependency>
        <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>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR5</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>

Spring Boot Maven 插件提供了很多便捷的特性:

  • 将 classpath 里面所有用到的 jar 包构建成一个可执行的 JAR 文件,使得运行和发布你的服务变得更加便捷。
  • 搜索 public static void main()方法并且将它标记为可执行类。
  • 它还提供了一个内置的依赖解析器,可以自动调整版本号与 Spring Boot 的依赖相一致。你可以覆盖其中的任何一个版本,但是默认情况下它会使用Spring Boot自身版本集中的版本。

通过IDE构建项目

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

书店服务将有一个端点。它可以访问/recommended,并(为了简单)返回一个String推荐的阅读列表。

BookstoreApplication.java中编辑你的主类。应该看起来像这样:

bookstore/src/main/java/hello/BookstoreApplication.java

package hello;

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 String readingList(){
    return "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

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

阅读应用程序将是我们的书店应用服务的前端(我们可以看到)我们将能够通过/to-read查看我们的阅读列表,并将从书店服务应用程序检索该阅读列表。

reading/src/main/java/hello/ReadingApplication.java

package hello;

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.client.RestTemplate;
import java.net.URI;

@RestController
@SpringBootApplication
public class ReadingApplication {

  @RequestMapping("/to-read")
  public String readingList() {
    RestTemplate restTemplate = new RestTemplate();
    URI uri = URI.create("http://localhost:8090/recommended");

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

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

要从书店服务获取列表,我们使用Spring的RestTemplate模板类。当我们使用它时,RestTemplate会向书店服务的URL发出HTTP GET请求,然后以String形式返回结果。(有关使用Spring来使用RESTful服务的更多信息,请参阅)使用RESTful Web Service指南 。)

将 server.port 属性添加到 src/main/resources/application.properties: reading/src/main/resources/application.properties

server.port=8080

我们现在可以在浏览器中访问我们的阅读应用程序的to-read端点,并查看我们的阅读列表。然而,由于我们依靠书店服务BookService,如果发生任何事情,或者如果阅读完全无法访问书店服务BookService,我们将没有列表,我们的用户将收到一个讨厌的HTTP 500错误消息。

应用断路器模式

Netflix的Hystrix库提供了断路器模式的实现:当我们将一个断路器应用于一个方法时,Hystrix会监控该方法的失败情况,如果故障达到阈值,Hystrix将打开断路器,以便后续访问该方法自动失败。当断路器打开时,Hystrix会重新调用该方法,并将它们传递到我们指定的回退方法。

Spring Cloud Netflix Hystrix查找使用@HystrixCommand注解的任何方法,并将该方法包装在连接到断路器的代理中,以便Hystrix可以监控它。 这当前只能在标有@Component@Service的类中工作,所以在阅读应用程序中,在src/main/java/hello下,添加一个新类:BookService

在创建BookService时,RestTemplate将被注入到BookService的构造函数中。完整的类应该是这样的:

reading/src/main/java/hello/BookService.java

package hello;

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注解作为它的回退方法是可靠的,所以如果由于某些原因HystrixreadingList()上打开断路器,我们将为我们的用户准备一个非常好的(如果是短的)占位符阅读列表。

在我们的主类ReadingApplication中,我们将创建一个RestTemplate bean,注入BookService并将其称为阅读列表:

reading/src/main/java/hello/ReadingApplication.java

package hello;

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

  @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(ReadingApplication.class, args);
  }
}

现在,要从书店服务检索列表,我们调用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应用程序,它使用断路器模式来防止级联故障,并为潜在的失败调用提供回退行为。

另请参阅

以下指南可能也有帮助:

本文由spring4all.com翻译小分队创作,采用知识共享-署名-非商业性使用-相同方式共享 4.0 国际 许可 协议进行许可。

评论 抢沙发

请登录后发表评论

    暂无评论内容