Spring Cloud(十五):Spring Cloud Gateway(续)

本文主要介绍 Spring Cloud Gateway 的路由熔断、路由重试和高可用。

路由熔断

在前面学习 Hystrix 的时候,我们知道 Hystrix 有服务降级的能力,即如果服务调用出现了异常,则执行指定的 fallback 方法。Spring Cloud Gateway 也融合了 Hystrix,可以为我们提供路由层面服务降级。我们就来看看如何来做。

在之前 gateway 工程的基础上引入 Hystrix 依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

配置文件添加 Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
gateway:
routes:
- id: service_customer
uri: lb://CONSUMER
predicates:
- Path=/customer/**
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback

Hystrix 支持两个参数:

  • name:即HystrixCommand的名字
  • fallbackUri:即 fallback 对应的 uri,这里的 uri 仅支持forward: schemed 的

最后在 gateway 的项目中创建一个 endpoint:/fallback

1
2
3
4
5
6
7
8
9
@RestController
public class FallbackController {

@GetMapping("/fallback")
public String fallback() {
return "Hello World!\nfrom gateway";
}

}

仅做以上配置就 OK 了,如果需要更强大的功能,可以参考HystrixGatewayFilterFactory进行自定义。

路由重试

虽然目前官方文档上还没有关于重试的内容,不过我们能在代码中找到RetryGatewayFilterFactory。但是据我测试这个好像还是有些问题。

理论上只要在 application.xml 里这么配置就可以了

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: service_customer
uri: lb://CONSUMER
predicates:
- Path=/customer/**
filters:
- Retry=5

为了测试我们再改造一下 consumer 中的 HelloController,我们让它在方法入口处打印 log,然后 sleep 10 分钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@CommonsLog
@RequestMapping("/hello")
@RestController
public class HelloController {

@Autowired
HelloRemote helloRemote;

@GetMapping("/{name}")
public String index(@PathVariable("name") String name) throws InterruptedException {
log.info("the name is " + name);
if ("retry".equals(name)) {
TimeUnit.MINUTES.sleep(10);
}
return helloRemote.hello(name) + "\n" + new Date().toString();
}

}

但是实际测试发现,当 Gateway 向 consumer 请求的时候,根本就没有超时这个概念,我也没找到能设置超时的地方。唯一能找到的一个超时是通过 Hystrix 设置(参考 github),比如上边我们HystrixCommand的 name 是 fallbackcmd,那么就设置如下

1
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000

但是配置了 Hystrix 之后,超时就直接 fallback 了,所以根本轮不到 retry

1
2
java.lang.NullPointerException: null
at org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory.lambda$null$0(RetryGatewayFilterFactory.java:55) ~[spring-cloud-gateway-core-2.0.0.RC1.jar:2.0.0.RC1]

那我 Hystrix 只配置 name 不配置 fallbackUri 行不行?也不行。

虽然这样确实 retry 了,在 consumer 的 log 中能看到多条打印,但是在 gateway 里边却报错了

1
com.netflix.hystrix.exception.HystrixRuntimeException: fallbackcmd command executed multiple times - this is not permitted.

所以看样子这个 Retry 现在还不能用,等有时间了再研究下。

高可用

我们实际使用 Spring Cloud Gateway 的方式如上图,不同的客户端使用不同的负载将请求分发到后端的 Gateway,Gateway 再通过 Eureka 调用后端服务,最后对外输出。因此为了保证 Gateway 的高可用性,前端可以同时启动多个 Gateway 实例进行负载,在 Gateway 的前端使用 Nginx 或者 F5 进行负载转发以达到高可用性。

参考

Spring Cloud Gatewa - Hystrix GatewayFilter Factory

示例代码可以从 Github 获取:https://github.com/zhaoyibo/spring-cloud-study