gateway 总体执行流程解析

目录

路由部分:

过滤器部分:


gateway 总体执行流程解析_第1张图片

这是Spring cloud gateway 的架构图,层次清晰,需要详细看的话可以直接全局搜索查看代码,里面用到了 Reactor 3 和 netty。

它和Spring Mvc看上去流程是相似的,Spring Mvc 是在 DispatchServlet 中获取 Handler,执行拦截器链,根据 Handler 获取 HandlerAdapter,然后解析请求参数,反射调用 Controller Method,得到执行结果,然后包装响应结果,直接返回 。而 gateway 走的是 Spring Webflux 的执行流程。

下面这是 Spring Webflux的核心,DispatcherHandler 请求分发器,handlerMappings,handlerAdapters,resultHandlers,这几乎和spring mvc是一样的,它会从handlerMappings中获得匹配的Route,再执行 invokeHandler(exchange, handler),它会选择走 SimpleHandlerAdapter的 handle方法,进入 FilteringWebHandler 类的 handle方法执行过滤器链,获取具体的微服务执行结果,返回结果。注意过滤器分为pre和post两种类型。   当一个请求到达网关被处理,它就是被 Spring Webflux 所处理,这个请求就是一个普通的请求,这就是网关的内部流程。

public class DispatcherHandler implements WebHandler, ApplicationContextAware {

	@Nullable
	private List handlerMappings;

	@Nullable
	private List handlerAdapters;

	@Nullable
	private List resultHandlers;

	...

    @Override
	public Mono handle(ServerWebExchange exchange) {
		if (this.handlerMappings == null) {
			return createNotFoundError();
		}
		return Flux.fromIterable(this.handlerMappings)
				.concatMap(mapping -> mapping.getHandler(exchange))
				.next()
				.switchIfEmpty(createNotFoundError())
				.flatMap(handler -> invokeHandler(exchange, handler))
				.flatMap(result -> handleResult(exchange, result));
	}

    private Mono invokeHandler(ServerWebExchange exchange, Object handler)       {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
				if (handlerAdapter.supports(handler)) {
					return handlerAdapter.handle(exchange, handler);
				}
			}
		}
		return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
	}

}

路由部分:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
      discovery:
        locator:
          enabled: true

routes是RouteDefinition,是路由的元信息,后面会组装为Route实例。就像Spring 里的BeanDefinition最终会实例化为一个Bean一样。Route里有一个异步predicate ,它会把Route里的所有普通 predicate 组合为一个 很长的异步predicate,Route里还有许多自定义的过滤器,需要自己配置。

gateway 总体执行流程解析_第2张图片

在 this.routeLocator.getRoutes() 的返回值是 Flux 类型,也就是说 Route 可以有0到n个,但因为 Route 是一开始就在配置文件中配置好的,所以它一开始就会查询出所有的Route,但实际上我们在配置文件中只定义了 user-service 这一个路由,ReactiveCompositeDiscoveryClient_user-service,而类似ReactiveCompositeDiscoveryClient_xxxx 是 gateway 为了和 nacos 注册中心做心跳保活的路由服务,我们不管。ServerWebExchange 是网关的上下文 Context ,保存了 Request,Response,Attribute 等信息。

下图就是做路由匹配的过程,我们的 只配置了 Path=/user/**,所以就进入 PathRoutePredicateFactory 的判断逻辑,不同的断言进入不同的判断逻辑。

gateway 总体执行流程解析_第3张图片

gateway 总体执行流程解析_第4张图片

 如果我们加了另一个断言 - After=2022-08-01T15:59:59Z[Asia/Shanghai], 它就会进入到一个异步断言中去,执行所有断言,并把它们的结果做一个 “与” 运算。

public interface AsyncPredicate extends Function>
        @Override
		public Publisher apply(T t) {
			return Flux.zip(left.apply(t), right.apply(t))
					.map(tuple -> tuple.getT1() && tuple.getT2());
		}
}

gateway 总体执行流程解析_第5张图片

开始执行过滤器链。

gateway 总体执行流程解析_第6张图片

过滤器部分:

责任链模式,就是遍历一个 List ,并把其中每一个 GatewayFilter 都执行一次。

private static class DefaultGatewayFilterChain implements GatewayFilterChain {

		private final int index;

		private final List filters;

		DefaultGatewayFilterChain(List filters) {
			this.filters = filters;
			this.index = 0;
		}

		private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
			this.filters = parent.getFilters();
			this.index = index;
		}

		public List getFilters() {
			return filters;
		}

		@Override
		public Mono filter(ServerWebExchange exchange) {
			return Mono.defer(() -> {
				if (this.index < filters.size()) {
					GatewayFilter filter = filters.get(this.index);
					DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
							this.index + 1);
					return filter.filter(exchange, chain);
				}
				else {
					return Mono.empty(); // complete
				}
			});
		}

	}

Gateway里有两种 Filter,一种是全局过滤器 GlobalFilter,作用在每一个 Route 里,另一种是局部过滤器 GatewayFilter,作用在某个 Route 或一组 Route 上,这时候就使用到适配器模式了。

public interface GlobalFilter { 
	Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
public interface GatewayFilter extends ShortcutConfigurable { 
	String NAME_KEY = "name"; 
	String VALUE_KEY = "value"; 
	Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

可以看到 GlobalFilter 和 GatewayFilter 都是函数式接口,而且接口签名都一样。

private static class GatewayFilterAdapter implements GatewayFilter {

		private final GlobalFilter delegate;

		GatewayFilterAdapter(GlobalFilter delegate) {
			this.delegate = delegate;
		}

		@Override
		public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
			return this.delegate.filter(exchange, chain);
		} 
	}

看得出,GatewayFilterAdapter 持有一个 GlobalFilter,但它会把这个GlobalFilter当成一个GatewayFilter 来使用,这就是适配器模式。

在loadFilters()这里完成全部的适配器转换。

private static List loadFilters(List filters) {
		return filters.stream().map(filter -> {
			GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
			if (filter instanceof Ordered) {
				int order = ((Ordered) filter).getOrder();
				return new OrderedGatewayFilter(gatewayFilter, order);
			}
			return gatewayFilter;
		}).collect(Collectors.toList());
	}

开始执行过滤器链,这时候会根据 lb://user-service 从注册中心获取到一个服务实例的ip和端口,这样就可以构造http请求,路由到具体的微服务实例去了,这时候用到的过滤器就是 LoadBalancerClientFilter 。

gateway 总体执行流程解析_第7张图片

具体选择哪个服务实例,主要是由Ribbon来做的,默认是 RoundRobin ,即轮询。

gateway 总体执行流程解析_第8张图片

构造好 http 请求后它会进入到  NettyRoutingFilter 过滤器,它会发送请求,并获得请求的输出结果,这里会使用到 netty 发送请求。这时候有点像 Open Feign,把构造好的 http 请求发送过去,然后接收响应。

public class NettyRoutingFilter implements GlobalFilter, Ordered {

	private static final Log log = LogFactory.getLog(NettyRoutingFilter.class);

	private final HttpClient httpClient;

    ...

	@Override
	@SuppressWarnings("Duplicates")
	public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);

		String scheme = requestUrl.getScheme();
		if (isAlreadyRouted(exchange)
				|| (!"http".equals(scheme) && !"https".equals(scheme))) {
			return chain.filter(exchange);
		}
		setAlreadyRouted(exchange);

		ServerHttpRequest request = exchange.getRequest();

		final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
		final String url = requestUrl.toASCIIString();

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

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

		boolean preserveHost = exchange
				.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);

		Flux responseFlux = httpClientWithTimeoutFrom(route)
				.headers(headers -> {
					headers.add(httpHeaders);
					// Will either be set below, or later by Netty
					headers.remove(HttpHeaders.HOST);
					if (preserveHost) {
						String host = request.getHeaders().getFirst(HttpHeaders.HOST);
						headers.add(HttpHeaders.HOST, host);
					}
				}).request(method).uri(url).send((req, nettyOutbound) -> {
					if (log.isTraceEnabled()) {
						nettyOutbound
								.withConnection(connection -> log.trace("outbound route: "
										+ connection.channel().id().asShortText()
										+ ", inbound: " + exchange.getLogPrefix()));
					}
					return nettyOutbound.send(request.getBody()
							.map(dataBuffer -> ((NettyDataBuffer) dataBuffer)
									.getNativeBuffer()));
				}).responseConnection((res, connection) -> {

					// Defer committing the response until all route filters have run
					// Put client response as ServerWebExchange attribute and write
					// response later NettyWriteResponseFilter
					exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
					exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);

					ServerHttpResponse response = exchange.getResponse();
					// put headers and status so filters can modify the response
					HttpHeaders headers = new HttpHeaders();

					res.responseHeaders().forEach(
							entry -> headers.add(entry.getKey(), entry.getValue()));

					String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
					if (StringUtils.hasLength(contentTypeValue)) {
						exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,
								contentTypeValue);
					}

					setResponseStatus(res, response);

					// make sure headers filters run after setting status so it is
					// available in response
					HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
							getHeadersFilters(), headers, exchange, Type.RESPONSE);

					if (!filteredResponseHeaders
							.containsKey(HttpHeaders.TRANSFER_ENCODING)
							&& filteredResponseHeaders
									.containsKey(HttpHeaders.CONTENT_LENGTH)) {
						// It is not valid to have both the transfer-encoding header and
						// the content-length header.
						// Remove the transfer-encoding header in the response if the
						// content-length header is present.
						response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
					}

					exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES,
							filteredResponseHeaders.keySet());

					response.getHeaders().putAll(filteredResponseHeaders);

					return Mono.just(res);
				});

		Duration responseTimeout = getResponseTimeout(route);
		if (responseTimeout != null) {
			responseFlux = responseFlux
					.timeout(responseTimeout, Mono.error(new TimeoutException(
							"Response took longer than timeout: " + responseTimeout)))
					.onErrorMap(TimeoutException.class,
							th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,
									th.getMessage(), th));
		}

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

 再往后按理说它会进入到 DispatcherHandler 执行结果处理,但 debug 没进来,在过滤器链里执行完,获取到结果后它就结束了。

private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
		if (this.resultHandlers != null) {
			for (HandlerResultHandler resultHandler : this.resultHandlers) {
				if (resultHandler.supports(handlerResult)) {
					return resultHandler;
				}
			}
		}
		throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
	}

GlobalFilter里有很多很丰富的过滤器,比如 RouteToRequestUrlFilter 可以将我们的请求前面的域名和端口转换成我们要调用的服务,并且判断了是否需要进行负载均衡。WebsocketRoutingFilter 可以处理"ws://","wss://"等 websocket协议。RequestRateLimiterGatewayFilter 可以处理请求限流。 HystrixGatewayFilter 可以处理熔断。 这就是网关各种功能的由来。




未完待续。。。

你可能感兴趣的:(gateway)