开始前
接下来分析的spring cloud gateway功能,是接入了服务发现。Spring Cloud Gateway在接入服务发现后,核心路由逻辑和通过property是有所不同的,核心实现类是不同的。
入口
Spring Cloud Gateway基于WebFlux实现,在webflux中mapping定义和org.springframework.web.reactive.HandlerMapping有关。
Spring Cloud Gateway可以根据请求前缀转发请求到同名的微服务,既然是gateway是基于webflux实现,又能接收接入spring cloud平台的所有微服务名为前缀的请求,那么spring cloud gateway一定实现了特殊的HandlerMapping,接收不同前缀的请求。所以接下来从HandlerMapping入手,分析spring cloud gateway核心路由功能。
核心流程
根据请求找到对应的 Route
在spring cloud gateway项目内,org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping实现了HandlerMapping接口,可以确定核心路由都是围绕此类展开。
以下代码是RoutePredicateHandlerMapping的getHandlerInternal方法,都是基于webflux的实现,代码逻辑很简单:
- 调用
lookupRoute,根据方法名是在寻找路由。 - 如果找到了路由,返回
webHandler对象,该对象为FilteringWebHandler实例,由构造器传入。 - 如果找不到返回
Mono.empty()
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
...
return lookupRoute(exchange)
.flatMap((Function<Route, Mono<?>>) r -> {
...
// 直接返回了 webHandler 对象,该对象在 spring cloud gateway 项目内为
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
// 这里很重要!!
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
return Mono.just(webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
...
})));
}
以下内容很重要
===========================================================================
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);这段代码非常重要,首先lookupRoute看方法名意思就是根据请求,找到对应的Route,用来路由请求。如果lookupRoute找到了Route,Route对象会被放入exchange对象内,这一步非常重要!!
除了lookupRoute外,可以看到getHandlerInternal方法返回了一个webHandler对象,在HandlerMapping接口里,getHandler方法需要返回一个handler,用来处理请求,在spring cloud gateway内,所有的请求都最后由webHandler处理,这里也非常的重要,之后会分析webHandler逻辑。
===========================================================================
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
// 通过 routeLocator 获取所有的 routes
return this.routeLocator.getRoutes()
.concatMap(route -> Mono.just(route).filterWhen(r -> {
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
// 当前 route 是否匹配,不匹配过滤掉
return r.getPredicate().apply(exchange);
})
.doOnError(e -> logger.error(
"Error applying predicate for route: " + route.getId(), e))
.onErrorResume(e -> Mono.empty()))
// 得到第一个匹配的结果
.next()
.map(route -> {
...
validateRoute(route, exchange);
return route;
});
}
在lookupRoute方法内,通过调用routeLocator.getRoutes获取所有的路由规则,再根据请求判断是否和route匹配,如果匹配返回,如果都没有匹配的route,返回Mono.empty()。
所以关键又来到了routeLocator对象,该对象由构造器传入,在GatewayAutoConfiguration可以找到,RoutePredicateHandlerMapping bean创建时,routeLocator由外部注入,在同一个配置类内,可以找到RouteLocator bean的定义方法。
@Bean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(
FilteringWebHandler webHandler, RouteLocator routeLocator,
GlobalCorsProperties globalCorsProperties, Environment environment) {
return new RoutePredicateHandlerMapping(webHandler, routeLocator,
globalCorsProperties, environment);
}
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
// 这里通过 CompositeRouteLocator 串联了多个`RouteLocator`
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
在cachedCompositeRouteLocator bean定义方法内,也需要RouteLocator bean。可以在spring cloud gateway项目中找对应的RouteLocator实现,除去包装串联实现类外,只有一个org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator实现,很明显它就是获取所有route的核心类。
RouteDefinitionRouteLocator实现了RouteLocator的getRoutes方法,如下
@Override
public Flux<Route> getRoutes() {
Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute);
...
return routes.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
通过routeDefinitionLocator.getRouteDefinitions获取所有的route定义,再转化为Route对象。
RouteDefinitionLocator有多个实现类,除去包装缓存实现类和串联实现类外,还有基于property和基于服务发现的实现。由于在文章开头已经描述,是在接入服务发现的情况下,所以接下里只需要关注服务发现的实现类DiscoveryClientRouteDefinitionLocator。
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
...
// serviceInstances 为多个微服务实例
return serviceInstances
...
.map(instance -> {
// 根据 instance 创建 RouteDefinition
RouteDefinition routeDefinition = buildRouteDefinition(urlExpr,
instance);
final ServiceInstance instanceForEval = new DelegatingServiceInstance(
instance, properties);
... // 从 application.yml 获取 predicate 和 filter 的逻辑在这里不展开,注释了
return routeDefinition;
});
}
DiscoveryClientRouteDefinitionLocator.getRouteDefinitions()方法内,buildRouteDefinition方法根据微服务实例信息,构建RouteDefinition,那么微服务实例哪里来?答案如下
public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient,
DiscoveryLocatorProperties properties) {
this(discoveryClient.getClass().getSimpleName(), properties);
// 从注册中心获取所有的微服务信息
serviceInstances = discoveryClient.getServices()
.flatMap(service -> discoveryClient.getInstances(service).collectList());
}
在DiscoveryClientRouteDefinitionLocator构造器内,从注册中心获取了所有的注册的微服务实例。
根据以上的分析的内容可以总结以下几点:
RouteDefinitionLocator内会从注册中心获取所有的微服务实例,并转化为RouteDefinition。RouteLocator内会将RouteDefinition转化为Route。RoutePredicateHandlerMapping内根据接收到的请求匹配,找到对应的Route。- 将
Route对象放入exchange请求对象内。 - 返回
webHandler。
根据 Route 处理请求
在上一个小节里讲到了webHandler,由于RoutePredicateHandlerMapping的getHandlerInternal方法内,不管找到的Route是什么,最后都会返回webHandler对象,所以所有的请求都在webHandler内处理。
在Spring Cloud Gateway内,webHandler接口的实现类为FilteringWebHandler。
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
// 在 RoutePredicateHandlerMapping 被放入 exchange 对象内的 Route 在 webHandler 内被取出
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
List<GatewayFilter> gatewayFilters = route.getFilters();
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
combined.addAll(gatewayFilters);
AnnotationAwareOrderComparator.sort(combined);
// 执行 filter
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
handle方法包含了对请求处理的所有逻辑:
- 从
exchange对象内取出Route对象。 - 从
Route获取所有filter,合并全局filter。 - 链式处理
exchange,就是处理请求的过程。
在spring cloud gateway的设计中,请求处理被设计为链式处理流程,可以通过实现GatewayFilter接口,实现对请求的处理。
在spring cloud gateway项目内,有非常非常多的GatewayFilter实现,包括熔断,限速,重试,header处理等等,不在详细分析。
filter最重要的逻辑处理为:
- 从请求里获取目标调用服务名。
- 通过
LoadBalanced根据服务名获取请求IP。 - 默认通过
netty路由请求。 - 返回结果写入
exchange response。 - 路由完成。
总结
通过上面的分析,spring cloud gateway的实现逻辑很简单,代码逻辑非常清晰,非常优雅。
核心功能上就分为2点:
- 根据请求和注册中心获取的微服务实例获取路由信息。
- 处理转发请求。
请求处理逻辑通过filter方式,链式处理请求,又清晰又好理解。总结一个字就是"巧"!
本文深入解析了SpringCloud Gateway如何利用服务发现实现路由,重点讲解了RoutePredicateHandlerMapping、RouteDefinitionRouteLocator和FilteringWebHandler的工作原理。核心功能在于根据请求和注册中心动态路由到对应微服务,请求处理则通过filter链式进行。
1095

被折叠的 条评论
为什么被折叠?



