【深入理解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写回给客户端。

相关推荐
Geoking.20 小时前
【设计模式】策略模式(Strategy)详解:把 if-else 变成可切换的算法
java·设计模式·策略模式
代码改变生活-12020 小时前
idea 清除缓存之后重启项目编译失败
java·缓存·intellij-idea
fisher_sky20 小时前
流媒体服务mediamtx和FFMpeg工具链联合实验
后端
Microsoft Word20 小时前
HashMap面试题总结
java·开发语言
stillaliveQEJ20 小时前
【MyBatis】DML映射
java·mybatis
qq_124987075320 小时前
基于SSM框架的智能密室逃脱信息管理系统(源码+论文+部署+安装)
java·大数据·人工智能·spring boot·后端·毕业设计·计算机毕业设计
ekkcole20 小时前
java实现对excel文件合并单元格(只针对文件)
java·开发语言·excel
no245441020 小时前
RAGFlow 全面接入 MinerU 2.0,支持 pipeline、vlm-transformers、vlm-sglang 三种模式,解析精度大幅度up
java·大数据·人工智能·python·ai·sglang
lkbhua莱克瓦2420 小时前
MySQL事务隔离级别:从并发混乱到数据一致性守护者
java·数据库·mysql·隔离级别