💨 作者:
laker
,因为喜欢LOL滴神faker
,又是NBA湖人队🏀(laker
)粉丝儿(主要是老詹的粉丝儿),本人又姓李,故取笔名:laker
❤️喜欢分享自己工作中遇到的问题和解决方案,以及一些读书笔记和心得分享。
🌰本人创建了微信公众号【Java大厂面试官】,用于和大家交流分享
🏰 个人微信【lakernote】,加作者备注下暗号:cv之道
。
文章目录
本文Spring Cloud Gateway 版本:2020.0.0
前言
之前讲过了Spring Cloud Gateway内置的11种路由谓词工厂,但是有些场景,还是不够我们用,我们想自定义实现路由谓词工厂,在项目开发中自由的掌控雷电。
谓词剖析
简而言之,Spring Cloud Gateway中的谓词是一个对象,用于测试给定请求是否满足给定条件。对于每个路由,我们可以定义一个或多个谓词,如果满足,将在应用任何过滤器后接受对配置的后端的请求。
在编写谓词之前,让我们看一下现有谓词的源代码,或更准确地说,是现有PredicateFactory的代码*。顾名思义,Spring Cloud Gateway使用流行的Factory Method Pattern作为一种机制,以可扩展的方式支持创建谓词*实例。
我们可以选择任何一个内置谓词工厂,它们可以在spring-cloud-gateway-core模块的org.springframework.cloud.gateway.handler.predicate包中找到。我们可以很容易地发现现有名称,因为它们的名称都以RoutePredicateFactory结尾。HeaderRouterPredicateFactory是一个很好的例子:
public class HeaderRoutePredicateFactory extends
AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {
// ... setup code omitted
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// ... predicate logic omitted
}
};
}
@Validated
public static class Config {
public Config(boolean isGolden, String customerIdCookie ) {
// ... constructor details omitted
}
// ...getters/setters omitted
}
}
在实现过程中,我们可以观察到一些关键点:
- 它扩展了AbstractRoutePredicateFactory ,进而实现了网关使用的RoutePredicateFactory接口。
- 该应用方法返回实际的实例*-谓语一个GatewayPredicate*在这种情况下,
- 谓词定义内部 Config类,该类用于存储测试逻辑使用的静态配置参数
如果我们看一下其他可用的PredicateFactory, 我们将看到基本模式基本相同:
- 定义一个Config类来保存配置参数
- 使用配置类作为其模板参数扩展 AbstractRoutePredicateFactory
- 覆盖 apply方法,返回实现所需测试逻辑的 谓词
实施自定义谓词工厂
对于我们的实现,我们假设以下情况:对于给定的API,调用我们必须在两个可能的后端之间进行选择。我们最重视的“黄金”客户应该被路由到功能强大的服务器,该服务器可以访问更多的内存,更多的CPU和快速的磁盘。非黄金客户使用功能较弱的服务器,这会导致响应时间变慢。
为了确定请求是否来自黄金客户,我们需要调用一个服务,该服务采用与请求关联的customerId并返回其状态。至于customerId,在我们的简单场景中,我们假设它在cookie中可用。
有了所有这些信息,我们现在可以编写我们的自定义谓词。我们将保留现有的命名约定,并将我们的类命名为GoldenCustomerRoutePredicateFactory:
public class GoldenCustomerRoutePredicateFactory extends
AbstractRoutePredicateFactory<GoldenCustomerRoutePredicateFactory.Config> {
private final GoldenCustomerService goldenCustomerService;
// ... constructor omitted
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return (ServerWebExchange t) -> {
List<HttpCookie> cookies = t.getRequest()
.getCookies()
.get(config.getCustomerIdCookie());
boolean isGolden;
if ( cookies == null || cookies.isEmpty()) {
isGolden = false;
} else {
String customerId = cookies.get(0).getValue();
isGolden = goldenCustomerService.isGoldenCustomer(customerId);
}
return config.isGolden() ? isGolden : !isGolden;
};
}
@Validated
public static class Config {
boolean isGolden = true;
@NotEmpty
String customerIdCookie = "customerId";
// ...constructors and mutators omitted
}
}
如我们所见,实现非常简单。我们的 apply方法返回一个lambda,该lambda使用传递给它的ServerWebExchange实现所需的逻辑 。首先,它检查customerId cookie的存在 。如果找不到它,那么这是普通客户。否则,我们使用cookie值来调用isGoldenCustomer服务方法。
接下来,我们将客户端的类型与配置的isGolden参数结合起来以确定返回值。这使我们可以通过更改isGolden的值来使用相同的谓词来创建上述两个路由。
注册自定义谓语工厂
对自定义谓词工厂进行编码后,我们需要一种使Spring Cloud Gateway知道是否存在的方法。由于我们使用的是Spring,因此可以通过通常的方式完成:我们声明类型为GoldenCustomerRoutePredicateFactory的bean 。
由于我们的类型通过Base类实现 RoutePredicateFactory ,因此它将在上下文初始化时由Spring选择并提供给Spring Cloud Gateway。
在这里,我们将使用*@Configuration*类创建bean :
@Configuration
public class CustomPredicatesConfig {
@Bean
public GoldenCustomerRoutePredicateFactory goldenCustomer(
GoldenCustomerService goldenCustomerService) {
return new GoldenCustomerRoutePredicateFactory(goldenCustomerService);
}
}
我们假定这里 在Spring的上下文中有一个合适的 GoldenCustomerService实现。在我们的例子中,我们只有一个虚拟的实现,该实现将customerId值与固定值进行比较 -不现实,但对演示有用。
使用自定义谓词
现在我们已经实现了“黄金客户”谓词,并且可用于Spring Cloud Gateway,我们可以开始使用它来定义路由。首先,我们将使用流畅的API定义路由,然后使用YAML以声明的方式进行操作。
1.使用Fluent API定义
当我们必须以编程方式创建复杂对象时,Fluent API是一种流行的设计选择。在我们的例子中,我们在*@Bean中定义路由,该@Bean使用RouteLocatorBuilder和我们的自定义谓词工厂创建一个RouteLocator*对象 :
@Bean
public RouteLocator routes(RouteLocatorBuilder builder, GoldenCustomerRoutePredicateFactory gf ) {
return builder.routes()
.route("golden_route", r -> r.path("/api/**")
.uri("https://fastserver")
.predicate(gf.apply(new Config(true, "customerId"))))
.route("common_route", r -> r.path("/api/**")
.uri("https://slowserver")
.predicate(gf.apply(new Config(false, "customerId"))))
.build();
}
注意我们在每个路由中如何使用两个不同的Config配置。在第一种情况下,第一个参数为true,因此当我们收到黄金客户的请求时,谓词也将评估为true。至于第二条路线,我们在构造函数中传递 false,因此对于非黄金客户,我们的谓词将返回 true 。
2.在YAML中定义
我们可以使用属性或YAML文件以声明的方式获得与以前相同的结果。在这里,我们将使用YAML,因为它更易于阅读:
spring:
cloud:
gateway:
routes:
- id: golden_route
uri: https://fastserver
predicates:
- Path=/api/**
- GoldenCustomer=true
- id: common_route
uri: https://slowserver
predicates:
- Path=/api/**
- name: GoldenCustomer
args:
golden: false
customerIdCookie: customerId
在这里,我们使用两个可用的选项来定义谓词,从而定义了与以前相同的路由。第一个,golden_route使用紧凑的表示形式,形式为Predicate = [param [,param] +]。 谓词是谓词的名称,它是通过删除RoutePredicateFactory后缀从工厂类名称中自动得出的 。在“ =”符号之后,我们有用于填充关联的Config实例的参数。
当我们的谓词只需要简单的值时,这种紧凑的语法就可以了,但是并非总是如此。对于这些情况,我们可以使用第二种方法中描述的长格式。在这种情况下,我们为对象提供两个属性: name和 args。name包含谓词名称,而args用于填充 Config 实例。由于这次args是一个对象,因此我们的配置可以根据需要复杂。
测试
现在,让我们使用curl测试我们的网关来检查一切是否按预期工作 。对于这些测试,我们已经像之前显示的那样设置了路由,但是我们将使用公共可用的*httpbin.org*服务作为我们的虚拟后端。这是一项非常有用的服务,我们可以使用它来快速检查我们的规则是否按预期运行,既可以在线使用又可以作为我们可以在本地使用的docker映像使用。
我们的测试配置还包括标准的AddRequestHeader过滤器。我们使用它向 请求添加自定义Goldencustomer标头,该标头的值与谓词结果相对应。我们还添加了 StripPrefix过滤器,因为我们想 在调用后端之前从请求URI中删除/ api。
首先,让我们测试“普通客户”方案。在网关启动并运行后,我们使用curl调用httpbin的 标头API,该API将简单地回显所有接收到的标头:
$ curl http://localhost:8080/api/headers
{
"headers": {
"Accept": "*/*",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"127.0.0.1:51547\"",
"Goldencustomer": "false",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1",
"X-Forwarded-Host": "localhost:8080",
"X-Forwarded-Prefix": "/api"
}
}
如预期的那样,我们看到Goldencustomer 标头是用错误的值发送的。现在让我们与“金”客户一起尝试:
$ curl -b customerId=baeldung http://localhost:8080/api/headers
{
"headers": {
"Accept": "*/*",
"Cookie": "customerId=baeldung",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"127.0.0.1:51651\"",
"Goldencustomer": "true",
"Host": "httpbin.org",
"User-Agent": "curl/7.55.1",
"X-Forwarded-Host": "localhost:8080",
"X-Forwarded-Prefix": "/api"
}
}
这次,Goldencustomer 为true,因为我们发送了一个customerId cookie,其值被我们的虚拟服务识别为对黄金客户有效。
翻译于:
https://www.baeldung.com/spring-cloud-gateway-routing-predicate-factories
Spring Cloud 相关系列文章目录
网关服务
Spring Cloud Gateway
QQ群【837324215】
关注我的公众号【Java大厂面试官】,回复:常用工具、资源等关键词(更多关键词,关注后注意提示信息)获取更多免费资料。
公众号也会持续输出高质量文章,和大家共同进步。