Route、Predicate与Filter的关系

Spring-Cloud-Gateway源码系列学习

版本 v2.2.6.RELEASE

Route、Predicate、Filter关系总览

Route、Predicate、Filter是Spring-Cloud-Gateway里面非常核心的三个概念,其中Route是Spring-Cloud-Gateway处理的基本单位,Route里面是包含Predicat和Filter的,都是一对多的关系。Filter分为GatewayFilter和GlobalFilter,其中GlobalFilter会作用于每个Route。以一个请求来简单描述它们的作用时机就是:

img

Route源码解析

public class Route implements Ordered {

	private final String id;

	//路由目标uri,最终被转发的目的地
	private final URI uri;

	//序号,越小优先级越高,实现Ordered的getOrder方法返回order
	private final int order;

    //谓语,匹配Route的前置条件
	private final AsyncPredicate<ServerWebExchange> predicate;
	
    //过滤器列表,比如可以修改请求头等
	private final List<GatewayFilter> gatewayFilters;

    //元信息,有两个值可以设置分别是response-timeout和connect-timeout
    //@see org.springframework.cloud.gateway.support.RouteMetadataUtils
	private final Map<String, Object> metadata;
}

Route与Predicate关系之Predicate是如何被装载到Route的

以RouteDefinitionRouteLocator#combinePredicates分析

private AsyncPredicate<ServerWebExchange> combinePredicates(
		RouteDefinition routeDefinition) {
    //获取Map<String,String>裸的谓词配置信息
	List<PredicateDefinition> predicates = routeDefinition.getPredicates();
	if (predicates == null || predicates.isEmpty()) {
		// this is a very rare case, but possible, just match all
		return AsyncPredicate.from(exchange -> true);
	}
    //生产出第一个AsyncPredicate的谓词
	AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
			predicates.get(0));

	for (PredicateDefinition andPredicate : predicates.subList(1,
			predicates.size())) {
        //生产出下一个谓词
		AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
				andPredicate);
        //使用AsyncPredicate#and讲两个谓词合并为一个
		predicate = predicate.and(found);
	}
	//将所有谓词合并为一个后返回
	return predicate;
}

private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route,
		PredicateDefinition predicate) {
    //获取对应的谓词工厂
	RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
	if (factory == null) {
		throw new IllegalArgumentException(
				"Unable to find RoutePredicateFactory with name "
						+ predicate.getName());
	}
	if (logger.isDebugEnabled()) {
		logger.debug("RouteDefinition " + route.getId() + " applying "
				+ predicate.getArgs() + " to " + predicate.getName());
	}

    //构造谓词必要信息传给断言工厂
	// @formatter:off
	Object config = this.configurationService.with(factory)
			.name(predicate.getName())
			.properties(predicate.getArgs())
			.eventFunction((bound, properties) -> new PredicateArgsEvent(
					RouteDefinitionRouteLocator.this, route.getId(), properties))
			.bind();
	// @formatter:on

    //生产一个AsyncPredicate<ServerWebExchange>
	return factory.applyAsync(config);
}

Route与Filter关系之Filter是如何被装载到Route的

以RouteDefinitionRouteLocator#getFilters分析

private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
	List<GatewayFilter> filters = new ArrayList<>();

	// TODO: support option to apply defaults after route specific filters?
    //如果配置信息定义了defaultFilters,需要都加到filters里面
	if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
		filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
				new ArrayList<>(this.gatewayProperties.getDefaultFilters())));
	}

	if (!routeDefinition.getFilters().isEmpty()) {
		filters.addAll(loadGatewayFilters(routeDefinition.getId(),
				new ArrayList<>(routeDefinition.getFilters())));
	}

	AnnotationAwareOrderComparator.sort(filters);
	return filters;
}

List<GatewayFilter> loadGatewayFilters(String id,
		List<FilterDefinition> filterDefinitions) {
    //定义一个已经设置order的GatewayFilter列表
	ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
	for (int i = 0; i < filterDefinitions.size(); i++) {
		FilterDefinition definition = filterDefinitions.get(i);
        //获取指定GatewayFilterFactory
		GatewayFilterFactory factory = this.gatewayFilterFactories
				.get(definition.getName());
		if (factory == null) {
			throw new IllegalArgumentException(
					"Unable to find GatewayFilterFactory with name "
							+ definition.getName());
		}
		if (logger.isDebugEnabled()) {
			logger.debug("RouteDefinition " + id + " applying filter "
					+ definition.getArgs() + " to " + definition.getName());
		}

		// @formatter:off
        //构造GatewayFilter必要信息传给GatewayFilterFactory
		Object configuration = this.configurationService.with(factory)
				.name(definition.getName())
				.properties(definition.getArgs())
				.eventFunction((bound, properties) -> new FilterArgsEvent(
						// TODO: why explicit cast needed or java compile fails
						RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
				.bind();
		// @formatter:on

		// some filters require routeId
		// TODO: is there a better place to apply this?
		if (configuration instanceof HasRouteId) {
			HasRouteId hasRouteId = (HasRouteId) configuration;
            //如果含有routeId则设置进去
			hasRouteId.setRouteId(id);
		}

        //生产出GatewayFilter
		GatewayFilter gatewayFilter = factory.apply(configuration);
        //如果该GatewayFilter本身就实现Order,就直接添加到ordered列表
		if (gatewayFilter instanceof Ordered) {
			ordered.add(gatewayFilter);
		}
		else {
            //否则转成OrderedGatewayFilter,order遍历的下标+1
			ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
		}
	}

	return ordered;
}

Route与Predicate关系之Predicate是如何帮助HandlerMapping匹配到Route的

以RoutePredicateHandlerMapping#lookupRoute分析

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
	return this.routeLocator.getRoutes()
			// individually filter routes so that filterWhen error delaying is not a
			// problem
        	//使用filterWhen进行过滤,返回Mono<true>会留下
			.concatMap(route -> Mono.just(route).filterWhen(r -> {
				// add the current route we are testing
				//作者测试用途
				exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                //有了前面的合并AsyncPredicate,匹配Route只需要非常简单地apply,就直接把整个AsyncPredicate表达式都执行了
				return r.getPredicate().apply(exchange);
			})
					// instead of immediately stopping main flux due to error, log and
					// swallow it
					.doOnError(e -> logger.error(
							"Error applying predicate for route: " + route.getId(),
							e))
					.onErrorResume(e -> Mono.empty()))
			// .defaultIfEmpty() put a static Route not found
			// or .switchIfEmpty()
			// .switchIfEmpty(Mono.<Route>empty().log("noroute"))
        	//取一个
			.next()
			// TODO: error handling
			.map(route -> {
				if (logger.isDebugEnabled()) {
					logger.debug("Route matched: " + route.getId());
				}
				validateRoute(route, exchange);
                //返回匹配的Route
				return route;
			});

	/*
	 * TODO: trace logging if (logger.isTraceEnabled()) {
	 * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
	 */
}

Route与Filter关系之Filter是如何完成Route的愿望的

以NettyRoutingFilter#filter和NettyWriteResponseFilter#filter分析,其中NettyRoutingFilter是发送请求到Route的目标url,处理响应信息的status及header部分;NettyWriteResponseFilter负责处理响应信息的body部分,并发回给客户端。

public class NettyRoutingFilter implements GlobalFilter, Ordered {

	@Override
	@SuppressWarnings("Duplicates")
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		//拿出uri
		URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);

		//获取协议,如 https
		String scheme = requestUrl.getScheme();
		//如果已经处理了,或者 不是 http 也 不是 https
		if (isAlreadyRouted(exchange)
				|| (!"http".equals(scheme) && !"https".equals(scheme))) {
			//下一个filter
			/**
			 * @see ForwardRoutingFilter#filter(org.springframework.web.server.ServerWebExchange, org.springframework.cloud.gateway.filter.GatewayFilterChain)
			 */
			return chain.filter(exchange);
		}
		//设置为已经处理了
		setAlreadyRouted(exchange);

		//获取 request
		ServerHttpRequest request = exchange.getRequest();

		//获取请求方法, 如POST
		final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
		//获得ascii编码的url
		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 route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);

		//创建请求流
		Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange)
				.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) -> {
					//类似netty outbound, 可以在这里做编码工作
					if (log.isTraceEnabled()) {
						nettyOutbound
								.withConnection(connection -> log.trace("outbound route: "
										+ connection.channel().id().asShortText()
										+ ", inbound: " + exchange.getLogPrefix()));
					}
					return nettyOutbound.send(request.getBody().map(this::getByteBuf));
				}).responseConnection((res, connection) -> {
					/**
					 * 需要在这里做保存、处理header、status
					 *
					 * @see HttpClientResponse
					 */

					// Defer committing the response until all route filters have run
					// Put client response as ServerWebExchange attribute and write
					// response later NettyWriteResponseFilter

					//保存res到exchange上下文,流到NettyWriteResponseFilter的.then把响应结果发回客户端
					//把connection放到CLIENT_RESPONSE_ATTR
					/**
					 * @see NettyWriteResponseFilter#filter(org.springframework.web.server.ServerWebExchange, org.springframework.cloud.gateway.filter.GatewayFilterChain)
					 */
					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()));

					//获取数据类型,并保存到ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR
					String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
					if (StringUtils.hasLength(contentTypeValue)) {
						exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,
								contentTypeValue);
					}

					//把请求目标地址返回的status设置到exchange.getResponse
					setResponseStatus(res, response);

					// make sure headers filters run after setting status so it is
					// available in response
					//确保Response的HttpHeadersFilter在请求响应后执行
					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));
	}

	/**
	 * 把DataBuffer转成ByteBuf,用于netty请求目标地址
	 * @param dataBuffer
	 * @return
	 */
	protected ByteBuf getByteBuf(DataBuffer dataBuffer) {
		if (dataBuffer instanceof NettyDataBuffer) {
			NettyDataBuffer buffer = (NettyDataBuffer) dataBuffer;
			return buffer.getNativeBuffer();
		}
		// MockServerHttpResponse creates these
		else if (dataBuffer instanceof DefaultDataBuffer) {
			DefaultDataBuffer buffer = (DefaultDataBuffer) dataBuffer;
			return Unpooled.wrappedBuffer(buffer.getNativeBuffer());
		}
		throw new IllegalArgumentException(
				"Unable to handle DataBuffer of type " + dataBuffer.getClass());
	}

	/**
	 * 把请求目标地址返回的status设置到exchange.getResponse
	 * @param clientResponse
	 * @param response
	 */
	private void setResponseStatus(HttpClientResponse clientResponse,
			ServerHttpResponse response) {
		HttpStatus status = HttpStatus.resolve(clientResponse.status().code());
		if (status != null) {
			response.setStatusCode(status);
		}
		else {
			while (response instanceof ServerHttpResponseDecorator) {
				response = ((ServerHttpResponseDecorator) response).getDelegate();
			}
			if (response instanceof AbstractServerHttpResponse) {
				((AbstractServerHttpResponse) response)
						.setStatusCodeValue(clientResponse.status().code());
			}
			else {
				// TODO: log warning here, not throw error?
				throw new IllegalStateException("Unable to set status code "
						+ clientResponse.status().code() + " on response of type "
						+ response.getClass().getName());
			}
		}
	}

	/**
	 * Creates a new HttpClient with per route timeout configuration. Sub-classes that
	 * override, should call super.getHttpClient() if they want to honor the per route
	 * timeout configuration.
	 *
	 * 创建一个基于Netty的HttpClient
	 *
	 * @param route the current route.
	 * @param exchange the current ServerWebExchange.
	 * @param chain the current GatewayFilterChain.
	 * @return
	 */
	protected HttpClient getHttpClient(Route route, ServerWebExchange exchange) {
		Object connectTimeoutAttr = route.getMetadata().get(CONNECT_TIMEOUT_ATTR);
		if (connectTimeoutAttr != null) {
			Integer connectTimeout = getInteger(connectTimeoutAttr);
			return this.httpClient.tcpConfiguration((tcpClient) -> tcpClient
					.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout));
		}
		return httpClient;
	}

	/**
	 * 根据route的Metadata的CONNECT_TIMEOUT_ATTR获取连接超时时间
	 * @param connectTimeoutAttr
	 * @return
	 */
	static Integer getInteger(Object connectTimeoutAttr) {
		Integer connectTimeout;
		if (connectTimeoutAttr instanceof Integer) {
			connectTimeout = (Integer) connectTimeoutAttr;
		}
		else {
			connectTimeout = Integer.parseInt(connectTimeoutAttr.toString());
		}
		return connectTimeout;
	}

	/**
	 * 根据route获取响应超时时间
	 * @param route
	 * @return
	 */
	private Duration getResponseTimeout(Route route) {
		Object responseTimeoutAttr = route.getMetadata().get(RESPONSE_TIMEOUT_ATTR);
		Long responseTimeout = null;
		if (responseTimeoutAttr != null) {
			if (responseTimeoutAttr instanceof Number) {
				responseTimeout = ((Number) responseTimeoutAttr).longValue();
			}
			else {
				responseTimeout = Long.valueOf(responseTimeoutAttr.toString());
			}
		}
		return responseTimeout != null ? Duration.ofMillis(responseTimeout)
				: properties.getResponseTimeout();
	}

}
public class NettyWriteResponseFilter implements GlobalFilter, Ordered {

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		// NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_CONN_ATTR is not added
		// until the NettyRoutingFilter is run
		// @formatter:off
		/**
		 * @see ForwardPathFilter#filter(org.springframework.web.server.ServerWebExchange, org.springframework.cloud.gateway.filter.GatewayFilterChain)
		 */
		return chain.filter(exchange)
				.doOnError(throwable -> cleanup(exchange))
				.then(Mono.defer(() -> {
					/**
					 * 从 exchange 中拿到 connection
					 * @see NettyRoutingFilter#filter(org.springframework.web.server.ServerWebExchange, org.springframework.cloud.gateway.filter.GatewayFilterChain)
					 */
					Connection connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR);

					if (connection == null) {
						//如果connection为null,返回一个空的流
						return Mono.empty();
					}
					if (log.isTraceEnabled()) {
						log.trace("NettyWriteResponseFilter start inbound: "
								+ connection.channel().id().asShortText() + ", outbound: "
								+ exchange.getLogPrefix());
					}
					ServerHttpResponse response = exchange.getResponse();

					// TODO: needed?
					//创建body
					final Flux<DataBuffer> body = connection
							.inbound()
							.receive()
							.retain()
							.map(byteBuf -> wrap(byteBuf, response));

					//获取内容格式
					MediaType contentType = null;
					try {
						contentType = response.getHeaders().getContentType();
					}
					catch (Exception e) {
						if (log.isTraceEnabled()) {
							log.trace("invalid media type", e);
						}
					}
					//根据内容格式是不是流,采用不同处理
					return (isStreamingMediaType(contentType)
							? response.writeAndFlushWith(body.map(Flux::just))
							: response.writeWith(body));
				})).doOnCancel(() -> cleanup(exchange));
		// @formatter:on
	}

	//ByteBuf 转成 DataBuffer
	protected DataBuffer wrap(ByteBuf byteBuf, ServerHttpResponse response) {
		DataBufferFactory bufferFactory = response.bufferFactory();
		if (bufferFactory instanceof NettyDataBufferFactory) {
			NettyDataBufferFactory factory = (NettyDataBufferFactory) bufferFactory;
			return factory.wrap(byteBuf);
		}
		// MockServerHttpResponse creates these
		else if (bufferFactory instanceof DefaultDataBufferFactory) {
			DataBuffer buffer = ((DefaultDataBufferFactory) bufferFactory)
					.allocateBuffer(byteBuf.readableBytes());
			buffer.write(byteBuf.nioBuffer());
			byteBuf.release();
			return buffer;
		}
		throw new IllegalArgumentException(
				"Unkown DataBufferFactory type " + bufferFactory.getClass());
	}

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud Gateway 中获取参数可以使用 PredicateFilter 这两个组件来实现。下面给出两种常见的获取参数的方法: 1. 使用 Predicate 获取参数: 在路由配置中,可以使用 Predicate 来匹配请求,并从请求中获取参数。例如,可以使用 QueryRoutePredicateFactory 来匹配 URL 中的参数,并进行路由: ```yaml spring: cloud: gateway: routes: - id: example_route uri: http://example.com predicates: - Query=param=value ``` 上述配置将匹配具有 `param=value` 查询参数的请求,并将其路由到 `http://example.com`。 2. 使用 Filter 获取参数: 可以创建一个自定义的过滤器来获取请求中的参数。首先,实现 GlobalFilter 接口并重写过滤方法: ```java import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class CustomFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取请求中的参数 String paramValue = exchange.getRequest().getQueryParams().getFirst("param"); // 进行相应的处理 // ... return chain.filter(exchange); } @Override public int getOrder() { return 0; } } ``` 上述自定义过滤器可以获取请求中名为 `param` 的查询参数,并进行相应的处理。 通过 PredicateFilter,你可以根据自己的需求从请求中获取参数,并进行相应的处理。请根据你的具体场景选择适合的方法来获取参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值