在上一篇文章 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 将外部化配置的 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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
Kafka进阶篇知识点
Kafka高级篇知识点
44个Kafka知识点(基础+进阶+高级)解析如下
由于篇幅有限,小编已将上面介绍的**《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文档**