Spring Cloud Gateway 源码剖析之Route数据模型

在上一篇文章 Spring Cloud Gateway 源码剖析之配置初始化 中的 GatewayAutoConfiguration 核心配置类中,有提到此类。

@Bean

@Primary

public RouteDefinitionLocator routeDefinitionLocator(List routeDefinitionLocators) {

return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));

}

Gateway 提供多种方式来获取外部的配置,而 RouteDefinitionLocator 就是父接口,下面有多种实现。

在这里插入图片描述

1.4.1 PropertiesRouteDefinitionLocator

从配置文件(YML、Properties 等) 读取路由配置。代码如下:

public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {

private final GatewayProperties properties;

public PropertiesRouteDefinitionLocator(GatewayProperties properties) {

this.properties = properties;

}

public Flux getRouteDefinitions() {

// 从 GatewayProperties 获取路由配置数组。

return Flux.fromIterable(this.properties.getRoutes());

}

}

1.4.2 InMemoryRouteDefinitionRepository

从存储器(内存、Redis、MySQL 等)读取、保存、删除路由配置。InMemoryRouteDefinitionRepository 是基于内存的。

public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {

/**

  • 路由配置映射 通过此来保存route

  • key :路由编号 {@link RouteDefinition#id}

*/

private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap());

public InMemoryRouteDefinitionRepository() {

}

public Mono save(Mono route) {

return route.flatMap(® -> {

this.routes.put(r.getId(), r);

return Mono.empty();

});

}

public Mono delete(Mono routeId) {

return routeId.flatMap((id) -> {

if (this.routes.containsKey(id)) {

this.routes.remove(id);

return Mono.empty();

} else {

return Mono.defer(() -> {

return Mono.error(new NotFoundException("RouteDefinition not found: " + routeId));

});

}

});

}

public Flux getRouteDefinitions() {

return Flux.fromIterable(this.routes.values());

}

}

基于内存,通过 Map<String, RouteDefinition> routes 来保存 Route,缺点是如果重启,那么 route 会丢失。可以实现 RouteDefinitionRepository 接口自定义比如通过 Redis、Mysql 等来保存 Route。

1.4.3 DiscoveryClientRouteDefinitionLocator

获取在注册中心的服务列表,生成对应的 RouteDefinition 数组。

public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {

private static final Log log = LogFactory.getLog(DiscoveryClientRouteDefinitionLocator.class);

private final DiscoveryClient discoveryClient;

private final DiscoveryLocatorProperties properties;

private final String routeIdPrefix;

private final SimpleEvaluationContext evalCtxt;

public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {

this.discoveryClient = discoveryClient;

this.properties = properties;

if (StringUtils.hasText(properties.getRouteIdPrefix())) {

this.routeIdPrefix = properties.getRouteIdPrefix();

} else {

this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + “_”;

}

this.evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();

}

public Flux getRouteDefinitions() {

SpelExpressionParser parser = new SpelExpressionParser();

Expression includeExpr = parser.parseExpression(this.properties.getIncludeExpression());

Expression urlExpr = parser.parseExpression(this.properties.getUrlExpression());

Predicate includePredicate;

if (this.properties.getIncludeExpression() != null && !“true”.equalsIgnoreCase(this.properties.getIncludeExpression())) {

includePredicate = (instance) -> {

Boolean include = (Boolean)includeExpr.getValue(this.evalCtxt, instance, Boolean.class);

return include == null ? false : include;

};

} else {

includePredicate = (instance) -> {

return true;

};

}

// 获取discoveryClient,然后发起请求

Flux var10000 = Flux.fromIterable(this.discoveryClient.getServices());

DiscoveryClient var10001 = this.discoveryClient;

var10001.getClass();

return var10000.map(var10001::getInstances).filter((instances) -> {

return !instances.isEmpty();

}).map((instances) -> {

return (ServiceInstance)instances.get(0);

}).filter(includePredicate).map((instance) -> {

String serviceId = instance.getServiceId();

RouteDefinition routeDefinition = new RouteDefinition();

// 设置 ID

routeDefinition.setId(this.routeIdPrefix + serviceId);

// 设置 uri

String uri = (String)urlExpr.getValue(this.evalCtxt, instance, String.class);

routeDefinition.setUri(URI.create(uri));

ServiceInstance instanceForEval = new DiscoveryClientRouteDefinitionLocator.DelegatingServiceInstance(instance, this.properties);

Iterator var8 = this.properties.getPredicates().iterator();

Iterator var11;

Entry entry;

String value;

while(var8.hasNext()) {

PredicateDefinition originalx = (PredicateDefinition)var8.next();

// 添加 path 断言

PredicateDefinition predicate = new PredicateDefinition();

predicate.setName(originalx.getName());

var11 = originalx.getArgs().entrySet().iterator();

while(var11.hasNext()) {

entry = (Entry)var11.next();

value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);

predicate.addArg((String)entry.getKey(), value);

}

routeDefinition.getPredicates().add(predicate);

}

var8 = this.properties.getFilters().iterator();

while(var8.hasNext()) {

FilterDefinition original = (FilterDefinition)var8.next();

// 添加path 重写过滤器

FilterDefinition filter = new FilterDefinition();

filter.setName(original.getName());

var11 = original.getArgs().entrySet().iterator();

while(var11.hasNext()) {

entry = (Entry)var11.next();

value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);

filter.addArg((String)entry.getKey(), value);

}

routeDefinition.getFilters().add(filter);

}

return routeDefinition;

});

}

}

可以在官方的 GatewaySampleApplication 添加 Eureka 注册中心自行调试:

// 开启Eureka

@EnableDiscoveryClient

public class GatewaySampleApplication {

// … 省略其他代码

@Bean

public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {

return new DiscoveryClientRouteDefinitionLocator(discoveryClient);

}

}

当然要自己加入 Eureka 依赖以及配置文件

1.4.4 CachingRouteDefinitionLocator

public class CachingRouteDefinitionLocator implements RouteDefinitionLocator, ApplicationListener {

private final RouteDefinitionLocator delegate;

private final Flux routeDefinitions;

// 收集Route

private final Map<String, List> cache = new HashMap();

public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {

this.delegate = delegate;

FluxCacheBuilderMapMiss var10001 = CacheFlux.lookup(this.cache, “routeDefs”, RouteDefinition.class);

RouteDefinitionLocator var10002 = this.delegate;

var10002.getClass();

this.routeDefinitions = var10001.onCacheMissResume(var10002::getRouteDefinitions);

}

public Flux getRouteDefinitions() {

return this.routeDefinitions;

}

public Flux refresh() {

this.cache.clear();

return this.routeDefinitions;

}

public void onApplicationEvent(RefreshRoutesEvent event) {

this.refresh();

}

/** @deprecated */

@Deprecated

void handleRefresh() {

this.refresh();

}

}

1.4.5 CompositeRouteDefinitionLocator

组合多种 RouteDefinitionLocator 的实现,为 RouteDefinitionRouteLocator 提供统一入口。

public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {

// RouteDefinitionLocator 数组

private final Flux delegates;

public CompositeRouteDefinitionLocator(Flux delegates) {

this.delegates = delegates;

}

// 将组合的 delegates 的路由定义全部返回。

public Flux getRouteDefinitions() {

return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);

}

}

到此为止外部化的配置的多种方式全部解析完毕。接下来我们来看下编程方式。

2、编程方式

// org.springframework.cloud.gateway.sample.GatewaySampleApplication

@SpringBootConfiguration

@EnableAutoConfiguration

@Import(AdditionalRoutesImportSelector.class)

public class GatewaySampleApplication {

public static final String HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS = “hello from fake /actuator/metrics/gateway.requests”;

@Value(“${test.uri:http://httpbin.org:80}”)

String uri;

public static void main(String[] args) {

SpringApplication.run(GatewaySampleApplication.class, args);

}

@Bean

public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {

// @formatter:off

// String uri = “http://httpbin.org:80”;

// String uri = “http://localhost:9080”;

// ① RouteLocatorBuilder 可以构建多个路由信息。

return builder.routes()

// ② 指定了 Predicates,这里包含两个:

// 请求头Host需要匹配**.abc.org,通过 HostRoutePredicateFactory 产生。

// 请求路径需要匹配/anything/png,通过 PathRoutePredicateFactory 产生。

.route(r -> r.host(“**.abc.org”).and().path(“/anything/png”)

// ③ 指定了一个 Filter,下游服务响应后添加响应头X-TestHeader:foobar,

// 通过 AddResponseHeaderGatewayFilterFactory 产生。

.filters(f ->

f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “foobar”))

// ④ 指定路由转发的目的地 uri。

.uri(uri)

)

.route(“read_body_pred”, r -> r.host(“*.readbody.org”)

.and().readBody(String.class,

s -> s.trim().equalsIgnoreCase(“hi”))

.filters(f -> f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “read_body_pred”)

).uri(uri)

)

.route(“rewrite_request_obj”, r -> r.host(“*.rewriterequestobj.org”)

.filters(f -> f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “rewrite_request”)

.modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,

(exchange, s) -> {

return Mono.just(new Hello(s.toUpperCase()));

})

).uri(uri)

)

.route(“rewrite_request_upper”, r -> r.host(“*.rewriterequestupper.org”)

.filters(f -> f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “rewrite_request_upper”)

.modifyRequestBody(String.class, String.class,

(exchange, s) -> {

return Mono.just(s.toUpperCase() + s.toUpperCase());

})

).uri(uri)

)

.route(“rewrite_response_upper”, r -> r.host(“*.rewriteresponseupper.org”)

.filters(f -> f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “rewrite_response_upper”)

.modifyResponseBody(String.class, String.class,

(exchange, s) -> {

return Mono.just(s.toUpperCase());

})

).uri(uri)

)

.route(“rewrite_empty_response”, r -> r.host(“*.rewriteemptyresponse.org”)

.filters(f -> f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “rewrite_empty_response”)

.modifyResponseBody(String.class, String.class,

(exchange, s) -> {

if (s == null) {

return Mono.just(“emptybody”);

}

return Mono.just(s.toUpperCase());

})

).uri(uri)

)

.route(“rewrite_response_fail_supplier”, r -> r.host(“*.rewriteresponsewithfailsupplier.org”)

.filters(f -> f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “rewrite_response_fail_supplier”)

.modifyResponseBody(String.class, String.class,

(exchange, s) -> {

if (s == null) {

return Mono.error(new IllegalArgumentException(“this should not happen”));

}

return Mono.just(s.toUpperCase());

})

).uri(uri)

)

.route(“rewrite_response_obj”, r -> r.host(“*.rewriteresponseobj.org”)

.filters(f -> f.prefixPath(“/httpbin”)

.addResponseHeader(“X-TestHeader”, “rewrite_response_obj”)

.modifyResponseBody(Map.class, String.class, MediaType.TEXT_PLAIN_VALUE,

(exchange, map) -> {

Object data = map.get(“data”);

return Mono.just(data.toString());

})

.setResponseHeader(“Content-Type”, MediaType.TEXT_PLAIN_VALUE)

).uri(uri)

)

.route(r -> r.path(“/image/webp”)

.filters(f ->

f.prefixPath(“/httpbin”)

.addResponseHeader(“X-AnotherHeader”, “baz”))

.uri(uri)

)

.route(r -> r.order(-1)

.host(“**.throttle.org”).and().path(“/get”)

.filters(f -> f.prefixPath(“/httpbin”)

.filter(new ThrottleGatewayFilter()

.setCapacity(1)

.setRefillTokens(1)

.setRefillPeriod(10)

.setRefillUnit(TimeUnit.SECONDS)))

.uri(uri)

)

// ⑤ 创建RouteLocator实例

.build();

// @formatter:on

}

@Bean

public RouterFunction testFunRouterFunction() {

RouterFunction route = RouterFunctions.route(RequestPredicates.path(“/testfun”),

request -> ServerResponse.ok().body(BodyInserters.fromValue(“hello”)));

return route;

}

@Bean

public RouterFunction testWhenMetricPathIsNotMeet() {

RouterFunction route = RouterFunctions

.route(RequestPredicates.path(“/actuator/metrics/gateway.requests”), request -> ServerResponse.ok()

.body(BodyInserters.fromValue(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS)));

return route;

}

static class Hello {

String message;

Hello() {

}

Hello(String message) {

this.message = message;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

}

}

  • ① 使用 Builder 模式构建 Route

  • ② 创建 Predicates

  • ③ 创建 Filter

  • ④ 需要转发的目的地 uri

  • ⑤ 创建 RouteLocator 实例

// org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder.Builder#build

public RouteLocator build() {

return () -> {

return Flux.fromIterable(this.routes).map((routeBuilder) -> {

return routeBuilder.build(); // ①

});

};

}

// RouteLocator 是 Route 集合

public interface RouteLocator {

Flux getRoutes();

}

上面 build 方法返回的是 RouteLocator 对象,它是 Route 的集合所以上面 ① 中的 build 方法就是对应的 Route。

public Route build() {

Assert.notNull(this.id, “id can not be null”);

Assert.notNull(this.uri, “uri can not be null”);

AsyncPredicate predicate = this.getPredicate();

Assert.notNull(predicate, “predicate can not be null”);

return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters);

}

下面这个就是我们最重要的 Route 数据模型了,我们来看下是如何设计的吧。

public class Route implements Ordered {

// id,标识符,区别于其他 Route。

private final String id;

// destination uri,路由指向的目的地 uri,即客户端请求最终被转发的目的地。

private final URI uri;

// order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。

private final int order;

// predicate,谓语,表示匹配该 Route 的前置条件,即满足相应的条件才会被路由到目的地 uri。

private final AsyncPredicate predicate;

// gateway filters,过滤器用于处理切面逻辑,如路由转发前修改请求头等。

private final List gatewayFilters;

}

到这里,外部化配置的 Route、Predicate、Filter 会被映射成 RouteDefinition、FilterDefinition、PredicateDefinition,而编码方式会被映射成 Route、AsyncPredicate 、GatewayFilter。

有时候我们可能又有外部化配置又有编码方式的配置,那么这时候就需要有一个转换。那接下来就看一下 Gateway 是如何转换的。

四、RouteDefinitionRouteLocator


这里将 RouteDefinitionRouteLocator 单独作为一小节来说,RouteDefinitionRouteLocator 将外部化配置的 RouteDefinition、FilterDefinition、PredicateDefinition 转换成 Route、AsyncPredicate、GatewayFilter。

上一篇我们讲到的核心配置类 GatewayAutoConfiguration,给我提供了分析的入口,代码如下:

// ① GatewayProperties

// ② RouteDefinitionLocator

@Bean

public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List GatewayFilters, List predicates, RouteDefinitionLocator routeDefinitionLocator, @Qualifier(“webFluxConversionService”) ConversionService conversionService) {

// ③ 这里进行转换

return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties, conversionService);

}

1、①、② 在上面外部化配置讲过,这里就不重复阐述了。

2、③ 外部化配置进行转换,RouteDefinitionRouteLocator 是 RouteLocator 的实现

public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {

protected final Log logger = LogFactory.getLog(this.getClass());

public static final String DEFAULT_FILTERS = “defaultFilters”;

// RouteDefinition Locator,一个 RouteDefinitionLocator 对象。

private final RouteDefinitionLocator routeDefinitionLocator;

private final ConversionService conversionService;

/**

  • predicates factories,Predicate 工厂列表,会被映射成 key 为 name, value 为 factory 的 Map。

  • 可以猜想出 gateway 是如何根据 PredicateDefinition 中定义的 name 来匹配到相对应的 factory 了。

  • key :{@link RoutePredicateFactory#name()}

*/

private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();

/**

  • filter factories,Gateway Filter 工厂列表,同样会被映射成 key 为 name, value 为 factory 的 Map。

  • key :{@link GatewayFilterFactory#name()}

*/

private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap();

// gateway properties,外部化配置类。

private final GatewayProperties gatewayProperties;

private final SpelExpressionParser parser = new SpelExpressionParser();

private BeanFactory beanFactory;

private ApplicationEventPublisher publisher;

@Autowired

private Validator validator;

public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List predicates, List gatewayFilterFactories, GatewayProperties gatewayProperties, ConversionService conversionService) {

// 设置 RouteDefinitionLocator

this.routeDefinitionLocator = routeDefinitionLocator;

this.conversionService = conversionService;

// ① 初始化 RoutePredicateFactory

this.initFactories(predicates);

// ② 初始化 gatewayFilterFactories

gatewayFilterFactories.forEach((factory) -> {

GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);

});

// 设置 GatewayProperties

this.gatewayProperties = gatewayProperties;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

Kafka进阶篇知识点

image

Kafka高级篇知识点

image

44个Kafka知识点(基础+进阶+高级)解析如下

image

由于篇幅有限,小编已将上面介绍的**《Kafka源码解析与实战》、Kafka面试专题解析、复习学习必备44个Kafka知识点(基础+进阶+高级)都整理成册,全部都是PDF文档**

.name(), factory);

});

// 设置 GatewayProperties

this.gatewayProperties = gatewayProperties;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-NuBDiII5-1712107973486)]
[外链图片转存中…(img-8PyGFtJn-1712107973487)]
[外链图片转存中…(img-CXSsFUAT-1712107973487)]
[外链图片转存中…(img-f2NpW4HB-1712107973487)]
[外链图片转存中…(img-VAjQX3F5-1712107973488)]
[外链图片转存中…(img-9hW2ffjQ-1712107973488)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-xT4b80hq-1712107973489)]

Kafka进阶篇知识点

[外链图片转存中…(img-KjaxE60U-1712107973489)]

Kafka高级篇知识点

[外链图片转存中…(img-LUkO477v-1712107973489)]

44个Kafka知识点(基础+进阶+高级)解析如下

[外链图片转存中…(img-HzeaBmbU-1712107973490)]

由于篇幅有限,小编已将上面介绍的**《Kafka源码解析与实战》、Kafka面试专题解析、复习学习必备44个Kafka知识点(基础+进阶+高级)都整理成册,全部都是PDF文档**

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值