【深入理解SpringCloud微服务】Gateway源码解析

【深入理解SpringCloud微服务】Gateway源码解析

  • Gateway原理复习
  • 源码解析
    • [DispatcherHandler#handle(ServerWebExchange exchange)](#handle(ServerWebExchange exchange))
    • [RoutePredicateHandlerMapping#getHandlerInternal(ServerWebExchange exchange)](#getHandlerInternal(ServerWebExchange exchange))
    • [FilteringWebHandler#handle(ServerWebExchange exchange)](#handle(ServerWebExchange exchange))
    • 几个核心的过滤器
      • [LoadBalancerClientFilter#filter(ServerWebExchange exchange, GatewayFilterChain chain)](#filter(ServerWebExchange exchange, GatewayFilterChain chain))
      • [NettyRoutingFilter#filter(ServerWebExchange exchange, GatewayFilterChain chain)](#filter(ServerWebExchange exchange, GatewayFilterChain chain))

Gateway原理复习

Gateway 基于Spring-WebFlux,实现了WebFlux的两个核心组件HandleMapping和WebHandler。

  1. 首先HandleMapping会加载路由配置,并调用路由配置中的断言函数进行匹配,如果匹配成功,则使用该路由规则。
  2. WebHandler 会把路由配置中指定的过滤器(GatewayFilter )和全局过滤器(GlobalFilter)组装成过滤器链,然后调用过滤器链进行请求处理。

源码解析

DispatcherHandler#handle(ServerWebExchange exchange)

spring-cloud-gateway 是基于spring-webflu 的,而spring-webflux通过netty 接收请求,当接收到请求时,会调用 DispatcherHandler#handle(ServerWebExchange exchange) 该方法进行处理。

DispatcherHandler#handle(ServerWebExchange exchange)

java 复制代码
	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		if (this.handlerMappings == null) {
			return createNotFoundError();
		}
		return Flux.fromIterable(this.handlerMappings)
				// 通过HandlerMapping获取WebHandler
				.concatMap(mapping -> mapping.getHandler(exchange))
				.next()
				.switchIfEmpty(createNotFoundError())
				// 调用WebHandler进行请求处理
				.flatMap(handler -> invokeHandler(exchange, handler))
				// 处理返回结果
				.flatMap(result -> handleResult(exchange, result));
	}

".concatMap(mapping -> mapping.getHandler(exchange))" 这行代码会调用到Gateway的RoutePredicateHandlerMapping的**getHandlerInternal()**方法,返回一个FilteringWebHandler。

然后 ".flatMap(handler -> invokeHandler(exchange, handler))" 这行代码会调用到RoutePredicateHandlerMappinghandle() 方法。

我们下面分别看一下RoutePredicateHandlerMapping的getHandlerInternal方法和RoutePredicateHandlerMapping的handle方法。

RoutePredicateHandlerMapping#getHandlerInternal(ServerWebExchange exchange)

java 复制代码
	@Override
	protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
		...

		//寻找Route
		//首先获取配置文件里面的routes配置,得到RouteDefinition集合
		//然后RouteDefinition转换成Route,得到Route集合
		//然后根据Route中的Predicate#apply(exchange)进行过滤
		//返回匹配的一个Route
		return lookupRoute(exchange)
				.flatMap((Function<Route, Mono<?>>) r -> {
					...

					//绑定Route到请求上下文exchange中,gatewayRoute -> Route
					exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
					//返回一个FilteringWebHandler
					return Mono.just(webHandler);
				}).switchIfEmpty(...);
	}

getHandlerInternal() 方法首先调用lookupRoute(exchange) 方法寻找匹配的route 。然后把匹配到的route绑定到ServerWebExchange 对象中,最后返回一个FilteringWebHandler

RoutePredicateHandlerMapping#lookupRoute(ServerWebExchange exchange)

java 复制代码
	protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
		// 拿到配置文件中的routes配置信息 RouteDefinition
		// 然后转换成Route对象,然后一个Route集合
		return this.routeLocator.getRoutes()
				// 遍历route集合
				.concatMap(route -> Mono.just(route).filterWhen(r -> {
					...
					//r.getPredicate()获取到Route中的谓词Predicate
					//调用Predicate#apply方法判断是否符合谓词的条件
					return r.getPredicate().apply(exchange);
				})
						.doOnError(...)
						.onErrorResume(...)
				.next()
				.map(...);

	}

lookupRoute(exchange)方法首先调用routeLocator.getRoutes() 方法获取到Route 集合,然后遍历Route集合,调用route中的Predicate进行匹配,然后返回匹配的route。

RouteDefinitionRouteLocator#getRoutes()

java 复制代码
	@Override
	public Flux<Route> getRoutes() {
		//getRouteDefinitions():获取路由定义信息
		//this::convertToRoute:转换所有的RouteDefinition为Route
		return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)
				.map(...);
	}

getRoutes() 方法调用getRouteDefinitions() 获取到所有的RouteDefinition,然后将其转换成Route。

FilteringWebHandler#handle(ServerWebExchange exchange)

java 复制代码
	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		//从请求上下文获取Route
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
		//从Route获取所有的GatewayFilter
		List<GatewayFilter> gatewayFilters = route.getFilters();

		//添加全局过滤器到一个新的集合中
		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
		combined.addAll(gatewayFilters); //加入普通过滤器
		// 对过滤器进行排序
		AnnotationAwareOrderComparator.sort(combined);

		...

		//构建过滤器链,调用chain的filter(exchage)进行处理
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}

handle() 方法做的事情就是组装过滤器链,并调用过滤器链进行处理。

下面我们看看几个核心的过滤器。

几个核心的过滤器

LoadBalancerClientFilter#filter(ServerWebExchange exchange, GatewayFilterChain chain)

java 复制代码
	@Override
	@SuppressWarnings("Duplicates")
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
		String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
		
		...

		//通过ribbon去nacos获取对应微服务名的实例列表
		//通过负载均衡算法选取一个
		final ServiceInstance instance = choose(exchange);

		...

		URI uri = exchange.getRequest().getURI();

		...
		
		//通过LoadBalancerClient的reconstructURI方法重新组装url
		URI requestUrl = loadBalancer.reconstructURI(
				new DelegatingServiceInstance(instance, overrideScheme), uri);

		//重写后的url重新绑定到上下文的gateWayRequestUrl参数
		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
		//调用下一个filter
		return chain.filter(exchange);
	}

LoadBalancerClientFilter 的作用就是利用ribbon的负载均衡机制选出一个实例,然后根据负载均衡的结果重写url。

NettyRoutingFilter#filter(ServerWebExchange exchange, GatewayFilterChain chain)

java 复制代码
	@Override
	@SuppressWarnings("Duplicates")
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		//从上下文获取到重写后的url
		URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
		
		...
		// 请求对象
		ServerHttpRequest request = exchange.getRequest();
		
		// 请求方法
		final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
		// 请求url
		final String url = requestUrl.toASCIIString();

		HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);

		final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
		filtered.forEach(httpHeaders::set);

		...

		// 利用Netty的HttpClient向后端微服务发起请求
		Flux<HttpClientResponse> responseFlux = this.httpClient
				.chunkedTransfer(chunkedTransfer).request(method).uri(url)
				.send((req, nettyOutbound) -> {
					req.headers(httpHeaders);
					...
					return nettyOutbound.options(NettyPipeline.SendOptions::flushOnEach)
							.send(request.getBody()
									.map(dataBuffer -> ((NettyDataBuffer) dataBuffer)
											.getNativeBuffer()));
				})
				// 处理后端微服务的返回结果
				.responseConnection((res, connection) -> {
					exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
					exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);

					ServerHttpResponse response = exchange.getResponse();
					HttpHeaders headers = new HttpHeaders();

					// 后端微服务返回的响应头
					res.responseHeaders().forEach(
							entry -> headers.add(entry.getKey(), entry.getValue()));

					...

					// 后端微服务返回的状态码
					HttpStatus status = HttpStatus.resolve(res.status().code());
					if (status != null) {
						response.setStatusCode(status);
					}
					else if (response instanceof AbstractServerHttpResponse) {
						...
					}
					else {...}

					HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
							getHeadersFilters(), headers, exchange, Type.RESPONSE);

					...
					
					response.getHeaders().putAll(filteredResponseHeaders);

					return Mono.just(res);
				});

		...

		return responseFlux.then(chain.filter(exchange));
	}

NettyRoutingFilter 的作用就是获取到LoadBalancerClientFilter 重写后的url,利用NettyHttpClient向后端微服务发起请求,然后获取到后端微服务返回的结果并保存,让后面的Filter写回给客户端。

相关推荐
踏浪无痕2 小时前
流程引擎、工作流、规则引擎、编排系统、表达式引擎……天呐,我到底该用哪个?
后端·工作流引擎
悟能不能悟2 小时前
java list.addAll介绍
java·windows·list
Alsn862 小时前
30.登录用户名密码 RSA 加密传输-后端为java
java·开发语言
益达3212 小时前
IDEA 整合 Git 版本控制:提交、分支管理与冲突解决实操
java·intellij-idea
FAQEW2 小时前
若依微服务版(RuoYi-Cloud)本地启动全攻略
前端·后端·微服务·若依·二开
MoonBit月兔2 小时前
海外开发者实践分享:用 MoonBit 开发 SQLC 插件(其三)
java·开发语言·数据库·redis·rust·编程·moonbit
问道飞鱼2 小时前
【Rust编程知识】在 Windows 下搭建完整的 Rust 开发环境
开发语言·windows·后端·rust·开发环境
天呐草莓2 小时前
企业微信运维手册
java·运维·网络·python·微信小程序·企业微信·微信开放平台
2501_921649492 小时前
股票 API 对接, 接入德国法兰克福交易所(FWB/Xetra)实现量化分析
后端·python·websocket·金融·区块链