SpringCloudGateway原理——Gateway集成服务发现原理(基于代码)

开始前

接下来分析的spring cloud gateway功能,是接入了服务发现。Spring Cloud Gateway在接入服务发现后,核心路由逻辑和通过property是有所不同的,核心实现类是不同的。

入口

Spring Cloud Gateway基于WebFlux实现,在webfluxmapping定义和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接口,可以确定核心路由都是围绕此类展开。

以下代码是RoutePredicateHandlerMappinggetHandlerInternal方法,都是基于webflux的实现,代码逻辑很简单:

  1. 调用lookupRoute,根据方法名是在寻找路由。
  2. 如果找到了路由,返回webHandler对象,该对象为FilteringWebHandler实例,由构造器传入。
  3. 如果找不到返回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找到了RouteRoute对象会被放入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实现了RouteLocatorgetRoutes方法,如下

@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构造器内,从注册中心获取了所有的注册的微服务实例。

根据以上的分析的内容可以总结以下几点:

  1. RouteDefinitionLocator内会从注册中心获取所有的微服务实例,并转化为RouteDefinition
  2. RouteLocator内会将RouteDefinition转化为Route
  3. RoutePredicateHandlerMapping内根据接收到的请求匹配,找到对应的Route
  4. Route对象放入exchange请求对象内。
  5. 返回webHandler

根据 Route 处理请求

在上一个小节里讲到了webHandler,由于RoutePredicateHandlerMappinggetHandlerInternal方法内,不管找到的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方法包含了对请求处理的所有逻辑:

  1. exchange对象内取出Route对象。
  2. Route获取所有filter,合并全局filter
  3. 链式处理exchange,就是处理请求的过程。

spring cloud gateway的设计中,请求处理被设计为链式处理流程,可以通过实现GatewayFilter接口,实现对请求的处理。

spring cloud gateway项目内,有非常非常多的GatewayFilter实现,包括熔断,限速,重试,header处理等等,不在详细分析。

filter最重要的逻辑处理为:

  1. 从请求里获取目标调用服务名。
  2. 通过LoadBalanced根据服务名获取请求IP
  3. 默认通过netty路由请求。
  4. 返回结果写入exchange response
  5. 路由完成。

总结

通过上面的分析,spring cloud gateway的实现逻辑很简单,代码逻辑非常清晰,非常优雅。

核心功能上就分为2点:

  1. 根据请求和注册中心获取的微服务实例获取路由信息。
  2. 处理转发请求。

请求处理逻辑通过filter方式,链式处理请求,又清晰又好理解。总结一个字就是"巧"!

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值