本文使用的 Spring Cloud 版本为 2.1.2.RELEASE,如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
<version>2.1.2.RELEASE<version>
</dependency>
一、如何使用Gateway功能
通过YAML配置文件
spring:
cloud:
gateway:
enabled: true
routes:
- id: path_route
order: 500
predicates:
- Path=/demo/list
filters:
- name: Retry
args:
retries: 1
uri: http://127.0.0.1:8080/demo/list
通过RouteLocatorBuilder注册
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder
.routes()
.route("path_route", r -> r.order(500).path("/demo/list").filters(f -> f.retry(1)).uri("http://127.0.0.1:8080/demo/list"))
.build();
}
使用说明
上述两种方式完全等价,它们建立了相同的“路由规则(RouteDefinition)”,该规则定义如下:
- id为path_route
- 优先级order为500
- 断言器使用PathRoutePredicateFactory匹配/demo/list路径
- 拦截器使用RetryGatewayFilterFactory在失败时进行1次重试
- 匹配成功后请求uri为http://127.0.0.1:8080/demo/list地址。
由此可见,路由规则包含5个部分,分别是id、order、predicates、filters、uri。
- 在定义predicates时,可以通过继承AbstractRoutePredicateFactory实现自定义的断言器,如果自定义的断言器在YAML配置文件中使用,命名需要遵循特殊规范,如示例中的PathRoutePredicateFactory断言器,在文件用Path=/demo/list表示,“Path”为PathRoutePredicateFactory的“RoutePredicateFactory”的前缀名,“/demo/list”为参数。
- 在定义filters时,可以通过继承AbstractGatewayFilterFactory实现自定义拦截器,YAML配置文件中使用时,同样遵循上述命名规范和使用规则。
二、实现自定义的Gateway组件
Spring Cloud Gateway的核心功能由断言器、拦截器实现,下面将详细讲解如何实现自己的断言器和拦截器。
RoutePredicateFactory断言器工厂
1. 自定义的RoutePredicateFactory断言器工厂需要通过继承AbstractRoutePredicateFactory实现
@Component
public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {
public CustomRoutePredicateFactory() {
// 指定apply方法参数的类型,需要与AbstractRoutePredicateFactory的泛型一致
super(CustomRoutePredicateFactory.Config.class);
}
@Override
public ShortcutType shortcutType() {
// 使用yaml配置文件时的数据映射方式
return ShortcutType.GATHER_LIST;
}
@Override
public List<String> shortcutFieldOrder() {
// 使用GATHER_LIST映射方式时,指定对应List的变量名(即Config对象中的List变量)
return Collections.singletonList("list");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
// config的变量值,框架会根据yaml配置文件进行设置
return exchange -> {
// 自定义的断言规则,返回true则表示断言匹配成功
for (String pattern : config.getList()) {
if (exchange.getRequest().getURI().getPath().startsWith(pattern)) {
return true;
}
}
return false;
};
}
public static class Config {
private List<String> list = new ArrayList<>();
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
}
下面将详细说明示例代码中各个方法的作用:
- 构造方法CustomRoutePredicateFactory(),内部调用了super(configClass)方法,传入了CustomRoutePredicateFactory.Config.class对象,用于指定apply(config)方法参数的类型,框架根据该class类型自动生成config对象。
- shortcutType()方法,返回值类型ShortcutType共有三个属性,各个属性决定了框架自动生成config对象时的规则。当ShortcutType.DEFAULT类型时,框架将yaml中的值,映射到config中名称相同的变量中;当ShortcutType.GATHER_LIST类型时,框架将yaml中的值聚合为List,映射到shortcutFieldOrder()的返回值对应config的变量中,此时shortcutFieldOrder()返回的List的长度必须为1。
- shortcutFieldOrder()方法,当shortcutType()返回值为ShortcutType.GATHER_LIST时被使用,用于指定Config中List变量的名称。
- apply(config)方法,返回值Predicate为实际的RoutePredicate断言器。
2. 使用自定义的RoutePredicateFactory断言器工厂
2.1 通过YAML配置文件
需要注意自定义的断言器工厂命名必须为以RoutePredicateFactory作为后缀,而前面的部分为断言器工厂的name值。
spring:
cloud:
gateway:
enabled: true
routes:
- id: custom_route
predicates:
- name: Custom
args:
arg1: /demo/list
uri: http://127.0.0.1:8080/demo/list
2.2 通过RouteLocatorBuilder注册
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, CustomRoutePredicateFactory customRoutePredicateFactory) {
CustomRoutePredicateFactory.Config config = new CustomRoutePredicateFactory.Config();
config.setList(Lists.newArrayList("/demo/list"));
return builder
.routes()
.route("custom_route", r -> r.predicate(customRoutePredicateFactory.apply(config)).uri("http://127.0.0.1:8080/demo/list"))
.build();
}
3. 自定义Predicate断言器
3.1 通过RouteLocatorBuilder注册
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, CustomRoutePredicateFactory customRoutePredicateFactory) {
List<String> paths = new ArrayList<>();
paths.add("/demo/list");
return builder
.routes()
.route("path_route", r -> r.predicate(exchange -> {
for (String pattern : paths) {
if (exchange.getRequest().getURI().getPath().startsWith(pattern)) {
return true;
}
}
return false;
}).uri("http://127.0.0.1:8080/demo/list"))
.build();
}
由此可见,Predicate断言器是是Gateway匹配请求的核心,当Predicate.test(exchange)方法返回true时匹配成功。
GatewayFilterFactory拦截器工厂
1. 自定义的GatewayFilter拦截器需要通过继承AbstractGatewayFilterFactory实现。
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {
public CustomGatewayFilterFactory() {
super(CustomGatewayFilterFactory.Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return new CustomGatewayFilter(config);
}
public static class CustomGatewayFilter implements GatewayFilter, Ordered {
private Config config;
public CustomGatewayFilter(Config config) {
this.config = config;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (config.isGray()) {
return chain.filter(exchange);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 100;
}
}
public static class Config {
private boolean gray = false;
public boolean isGray() {
return gray;
}
public void setGray(boolean gray) {
this.gray = gray;
}
}
}
定义方式与RoutePredicateFactory基本相同,在需要对GatewayFilter设置优先级时需要实现Ordered接口。
2. 使用自定义的GatewayFilterFactory拦截器工厂
2.1 通过YAML配置文件
需要注意自定义的拦截器工厂命名必须为以GatewayFilterFactory作为后缀,而前面的部分为拦截器工厂的name值。
spring:
cloud:
gateway:
enabled: true
routes:
- id: custom_route
predicates:
- name: Custom
args:
arg1: /demo/list
filters:
- name: Custom
args:
gray: true
uri: http://127.0.0.1:8080/demo/list
使用方式与RoutePredicateFactory基本相同,但参数映射仅支持变量名方式。
GlobalFilter全局拦截器
1. 自定义的GlobalFilter拦截器需要通过继承GlobalFilter实现。
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
public static final Logger log = LoggerFactory.getLogger(CustomGlobalFilter.class);
@Override
public int getOrder() {
return 0;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getRawPath();
log.info("API访问记录 - url={}", path);
return chain.filter(exchange);
}
}
GlobalFilter无需工厂类,实现GlobalFilter接口即可。排序需要实现Ordered接口。
三、说明
- Spring Cloud Gateway的核心逻辑,将以阅读源码的方式在后续文章讲解。
- 网关服务实现负载均衡、流量控制、请求鉴权,也将在后续的文章中讲解。