15.2 定义断路器
在声明断路器之前,您需要为服务添加 Spring Cloud Netflix Hystrix 的依赖。在 Maven pom.xml 中的依赖项如下所示:
1
<dependency>
2
<groupId>org.springframework.cloud</groupId>
3
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
4
</dependency>
Copied!
作为 Spring Cloud 组件的一部分,您还需要声明依赖管理。在我写这篇文章的时候,最新的发布版本为 Finchley.SR1。因此,Spring Cloud 的版本应该设置一下,以下条目应出现在 pom.xml 文件的 <dependency-Management> 部分:
1
<properties>
2
...
3
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
4
</properties>
5
6
...
7
8
<dependencyManagement>
9
<dependencies>
10
<dependency>
11
<groupId>org.springframework.cloud</groupId>
12
<artifactId>spring-cloud-dependencies</artifactId>
13
<version>${spring-cloud.version}</version>
14
<type>pom</type>
15
<scope>import</scope>
16
</dependency>
17
</dependencies>
18
</dependencyManagement>
Copied!
注意,创建项目时在 Initializr 中也可以通过勾选 Hystrix 复选框来添加依赖。如果使用 Initializr 将 Hystrix 添加到项目中,则依赖管理部分会自动为您创建好。
有了 Hystrix 依赖,接下来需要做的就是启用 Hystrix。这通过对每个应用程序的主配置添加 @EnableHystrix 注解来实现。例如,要在 ingredient 服务中启用 Hystrix,您可以这样注解 IngredientServiceApplication 类:
1
@SpringBootApplication
2
@EnableHystrix
3
public class IngredientServiceApplication {
4
...
5
}
Copied!
此时,应用程序中启用了 Hystrix,但这只意味着可以声明断路器了。您还没有在任何一个方法上声明使用断路器。这就要通过使用 @HystrixCommand 注解来实现。
任何用 @HystrixCommand 注解的方法都将被声明为具有断路器切面。例如,考虑下面这个方法,使用负载平衡的 RestTemplate 从 ingredient 服务查询 Ingredient 实体:
1
public Iterable<Ingredient> getAllIngredients() {
2
ParameterizedTypeReference<List<Ingredient>> stringList =
3
new ParameterizedTypeReference<List<Ingredient>>() {};
4
return rest.exchange(
5
"http://ingredient-service/ingredients", HttpMethod.GET,
6
HttpEntity.EMPTY, stringList).getBody();
7
}
Copied!
exchange() 的调用可能会引起问题。如果没有在 Eureka 中注册 ingredient-service,或者由于一些原因请求失败了,那么将抛出 RestClientException 异常(未检查的异常)。因为异常没有使用 try/catch 处理,调用方必须处理这个异常。如果调用方不处理,那么它将继续向上抛出到调用堆栈的上游。如果上游也没有处理,错误就会级联抛出到任何上游微服务或客户端。
未捕获的异常在任何应用程序中都是一个挑战,在微服务中尤其如此。当涉及到失败时,微服务应该遵循维加斯规则——发生在微服务中的,只停留在微服务中。在 getAllIngredients() 方法上声明断路器以满足该规则。
您只需要对方法添加 @HystrixCommand 注解,然后提供一个降级方法。首先,让我们将 @HystrixCommand 添加到 getAllIngredients() 方法上:
1
@HystrixCommand(fallbackMethod="getDefaultIngredients")
2
public Iterable<Ingredient> getAllIngredients() {
3
...
4
}
Copied!
有一个断路器保护它不发生故障,现在 getAllIngredients() 方法是故障安全的。无论出于何种原因,从 getAllIngredients() 中抛出未捕获的异常,断路器都将捕获它们,并将方法调用重定向到 getDefaultIngredients() 方法。
降级方法可以做任何您想让他们做的事情,但是目的是,在最初使用的方法无法使用时,提供备份行为。降级方法的唯一规则是它要具有与原方法相同的签名(除了方法名)。
要满足此要求, getAllIngredients() 方法必须也没有请求参数,并返回 List<Ingredient>getAllIngredients() 满足该规则并返回默认列表数据:
1
private Iterable<Ingredient> getDefaultIngredients() {
2
List<Ingredient> ingredients = new ArrayList<>();
3
ingredients.add(new Ingredient(
4
"FLTO", "Flour Tortilla", Ingredient.Type.WRAP));
5
ingredients.add(new Ingredient(
6
"GRBF", "Ground Beef", Ingredient.Type.PROTEIN));
7
ingredients.add(new Ingredient(
8
"CHED", "Shredded Cheddar", Ingredient.Type.CHEESE));
9
return ingredients;
10
}
Copied!
现在,如果 getAllIngredients() 方法失败了,断路器将调用 getDefaultIngredients(),调用者将收到一个默认列表。
您可能想知道降级方法本身是否有断路器。尽管上面的 getDefaultIngredients() 实现不会出什么问题,但有可能其他实现会有潜在的失败点。在这种情况下,您可以为 getDefaultIngredients() 添加 @HystrixCommand,并提供另一个降级方法。事实上,如果需要,您可以堆叠尽可能多的降级方法。唯一的限制是在降级的底部,必须有一个方法不发生故障且不再需要断路器的堆栈。
复制链接