SpringCloud源码之API网关Gateway

一、概念:

在微服务架构下,每个微服务都有不同的IP地址和端口,前端在访问时就需要针对每个微服务单独访问,一旦新增或者调整微服务后,前端也需要做同步的修改,非常麻烦,而且为了校验访问请求是合法的,每个微服务都需要都需要做安全校验。引入API网关后,前端就只需要访问API网关就行了,里面的请求转发和一些其他的验权都可以放在API网关里面进行统一处理。因此API网关的职责一是路由功能,根据定义的请求规则和转化的规则,将传入的请求映射到不同的微服务进行处理,另一个职责就是针对请求信息在API网关进行统一验权。SpringCloud Gateway是一款非常优秀性能强劲的API网关。

SpringCloud Gateway接收请求的总入口是DispatcherHandler,有两个非常重要的核心概念,就是路由(Route)和过滤器(Filter)。

1、路由(Route):Route 是网关的基础元素,由 ID、目标 URI、断言、过滤器组成。当请求到达网关时,由 Gateway HandlerMapping 通过断言进行路由匹配(Mapping),当断言为真时,匹配到路由。

Route的定义如下:

arduino 复制代码
public class Route implements Ordered {
	private final String id;
	private final URI uri;
	private final int order;
	private final AsyncPredicate<ServerWebExchange> predicate;
	private final List<GatewayFilter> gatewayFilters;
	private final Map<String, Object> metadata;
	...........
}

在配置文件定义的Route信息在SpringCloud Gateway里面叫做RouteDefinition,其定义如下:

typescript 复制代码
public class RouteDefinition {
	private String id;
	private List<PredicateDefinition> predicates = new ArrayList<>();
	private List<FilterDefinition> filters = new ArrayList<>();
	private URI uri;
	private Map<String, Object> metadata = new HashMap<>();
	private int order = 0;
	...........
}

public class PredicateDefinition {
	private String name;
	private Map<String, String> args = new LinkedHashMap<>();
	............
}

public class FilterDefinition {
	private String name;
	private Map<String, String> args = new LinkedHashMap<>();
	................
}

2、过滤器:过滤器可以在请求到达业务代码之前执行一些业务上的处理。SpringCloud gateway中有两种过滤器,路由过滤器(接口为GatewayFilter)和全局过滤器(接口为GlobalFilter);

路由过滤器GatewayFilter的定义如下:

kotlin 复制代码
public interface GatewayFilter {
	/**
	 * Process the Web request and (optionally) delegate to the next
	 * {@code WebFilter} through the given {@link GatewayFilterChain}.
	 * @param exchange the current server exchange
	 * @param chain provides a way to delegate to the next filter
	 * @return {@code Mono<Void>} to indicate when request processing is complete
	 */
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
} 

全局过滤器GlobalFilter的定义如下:

kotlin 复制代码
public interface GlobalFilter {
	/**
	 * Process the Web request and (optionally) delegate to the next
	 * {@code WebFilter} through the given {@link GatewayFilterChain}.
	 * @param exchange the current server exchange
	 * @param chain provides a way to delegate to the next filter
	 * @return {@code Mono<Void>} to indicate when request processing is complete
	 */
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

二、使用Demo:

1、引入依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
//由于采用了基于注册中心的动态路由,而注册中心使用的是eureka,因此需要引入eureka的包
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2、配置文件application.yml:

yaml 复制代码
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      routes:
        #netty 路由过滤器,http或https开头
        - id: app1-route
          uri: http://www.baidu.com
          predicates:
            - Path=/app1/**
          filters:
            #转发请求时去掉1级前缀
            - StripPrefix=1

3、实现Filter:

kotlin 复制代码
@Configuration
public class AccessFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
    return Ordered.HIGHEST_PRECEDENCE;
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  
    //鉴权
    Mono<Void> check = checkAccess(exchange);
    return check != null ? check : chain.filter(exchange);
}

三、总流程:

首先来看一张官网上的SpringCloud Gateway的架构图:

1、客户端发送请求到Spring Cloud Gateway,也就是DispatcherHandler接收了请求,如果Gateway Handler Mapping 判定 这个请求和路由是匹配的,则这个请求会被发送到Gateway Web Handler。

2、Gateway Web Handler通过与该请求匹配的过滤器链执行该请求。

再具体点的流程图:

SpringCloud Gateway API网关的流程:

1、加载路由定义RouteDefinition的集合;

2、将RouteDefinition集合转换为Route实例集合;

3、DispatcherHandler根据URL请求匹配Route;

4、交由WebHandler(FilteringWebHandler)执行过滤器链;

5、执行业务代码,并处理返回结果数据;

下面将分别针对这个流程步骤进行解析。

四、加载路由定义RouteDefinition的集合:

RouteDefinition加载的接口是RouteDefinitionLocator,这个接口有多个实现,最常见的RouteDefinition分别是application.yml定义和从配置中心加载。

RouteDefinition接口的定义只有只有一个方法getRouteDefinitions,代码如下:

csharp 复制代码
public interface RouteDefinitionLocator {
	Flux<RouteDefinition> getRouteDefinitions();
}

1、application.yml定义的路由信息加载RouteDefinition:

比如上面的demo,就是在配置文件application.yml中定义路由信息的,其加载是由PropertiesRouteDefinitionLocator完成的。代码如下:

kotlin 复制代码
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
	private final GatewayProperties properties;

	public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
		this.properties = properties;
	}

	@Override
	public Flux<RouteDefinition> getRouteDefinitions() {
		return Flux.fromIterable(this.properties.getRoutes());
	}
}

@ConfigurationProperties("spring.cloud.gateway")
public class GatewayProperties {	
	private List<RouteDefinition> routes = new ArrayList<>();
	
	................
}

由GatewayProperties类可以看到SpringBoot启动时会将application.yml中定义的spring.cloud.gateway.routes下面的所有的信息都加载到routes中,PropertiesRouteDefinitionLocator直接引用GatewayProperties就可以读取到application.yml中定义的RouteDefinition。

2、从配置中心加载RouteDefinition:

从配置中心加载RouteDefinition是由DiscoveryClientRouteDefinitionLocator完成的。其定义和加载代码如下:

ini 复制代码
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
	//管理以spring.cloud.gateway.discovery.locator开头的属性定义
	private final DiscoveryLocatorProperties properties;
	
	private final String routeIdPrefix;
	
	//存储注册中心的所有服务实例
	private Flux<List<ServiceInstance>> serviceInstances;

	public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
		this(discoveryClient.getClass().getSimpleName(), properties);
		//通过discoveryClient.getServices和discoveryClient.getInstances获取配置中心中的所有服务实例
		serviceInstances = discoveryClient.getServices().flatMap(service -> discoveryClient.getInstances(service).collectList());
	}
	
	public Flux<RouteDefinition> getRouteDefinitions() {
		.........省略部分代码............

		return serviceInstances.filter(instances -> !instances.isEmpty())				
				.map(instance -> {
					//从注册中心的服务实例信息构造RouteDefinition
					RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);					

					//构造RouteDefinition的断言信息
					for (PredicateDefinition original : this.properties.getPredicates()) {
						PredicateDefinition predicate = new PredicateDefinition();
						predicate.setName(original.getName());
						for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
							String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
							predicate.addArg(entry.getKey(), value);
						}
						routeDefinition.getPredicates().add(predicate);
					}

					//构造RouteDefinition的过滤器链信息
					for (FilterDefinition original : this.properties.getFilters()) {
						FilterDefinition filter = new FilterDefinition();
						filter.setName(original.getName());
						for (Map.Entry<String, String> entry : original.getArgs()
								.entrySet()) {
							String value = getValueFromExpr(evalCtxt, parser,
									instanceForEval, entry);
							filter.addArg(entry.getKey(), value);
						}
						routeDefinition.getFilters().add(filter);
					}

					return routeDefinition;
				});
	}
	
	protected RouteDefinition buildRouteDefinition(Expression urlExpr, ServiceInstance serviceInstance) {
		String serviceId = serviceInstance.getServiceId();
		RouteDefinition routeDefinition = new RouteDefinition();
		routeDefinition.setId(this.routeIdPrefix + serviceId);
		String uri = urlExpr.getValue(this.evalCtxt, serviceInstance, String.class);
		routeDefinition.setUri(URI.create(uri));
		// add instance metadata
		routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata()));
		return routeDefinition;
	}
}
	
@ConfigurationProperties("spring.cloud.gateway.discovery.locator")
public class DiscoveryLocatorProperties {
	private boolean enabled = false;
	
	private String routeIdPrefix;

	private String includeExpression = "true";

	private String urlExpression = "'lb://'+serviceId";

	private boolean lowerCaseServiceId = false;

	private List<PredicateDefinition> predicates = new ArrayList<>();

	private List<FilterDefinition> filters = new ArrayList<>();
	................
}

1、DiscoveryClientRouteDefinitionLocator的构造函数中,会调用discoveryClient.getServices和discoveryClient.getInstances获取配置中心中的所有服务实例信息保存到serviceInstances中;

2、遍历serviceInstances,buildRouteDefinition依据每一个ServiceInstance构造一个RouteDefinition对象,predicates和filters信息则从DiscoveryLocatorProperties中读取,而DiscoveryLocatorProperties对于predicates和filters集合则只有定义而没有赋值,是不是很好奇他是怎么生成的,看看GatewayDiscoveryClientAutoConfiguration的中的配置就恍然大悟了:

less 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@AutoConfigureBefore(GatewayAutoConfiguration.class)
@AutoConfigureAfter(CompositeDiscoveryClientAutoConfiguration.class)
@ConditionalOnClass({ DispatcherHandler.class })
@EnableConfigurationProperties
public class GatewayDiscoveryClientAutoConfiguration {
	...........省略部分代码.........
	
	@Bean
	public DiscoveryLocatorProperties discoveryLocatorProperties() {
		DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
		properties.setPredicates(initPredicates());
		properties.setFilters(initFilters());
		return properties;
	}
	
	public static List<PredicateDefinition> initPredicates() {
		ArrayList<PredicateDefinition> definitions = new ArrayList<>();	
		PredicateDefinition predicate = new PredicateDefinition();
		predicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class));
		predicate.addArg("pattern", "'/'+serviceId+'/**'");
		definitions.add(predicate);
		return definitions;
	}
	
	public static List<FilterDefinition> initFilters() {
		ArrayList<FilterDefinition> definitions = new ArrayList<>();
		
		FilterDefinition filter = new FilterDefinition();
		filter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
		String regex = "'/' + serviceId + '/(?<remaining>.*)'";
		String replacement = "'/${remaining}'";
		filter.addArg("regexp", regex);
		filter.addArg("replacement", replacement);
		definitions.add(filter);

		return definitions;
	}
}

五、将RouteDefinition集合转换为Route实例集合:

RouteLocator接口用于将RouteDefinition转换为Route实例,这个接口只定义了一个方法getRoutes,代码如下:

csharp 复制代码
public interface RouteLocator {
	Flux<Route> getRoutes();
}

这个接口有三个实现类:CachingRouteLocator,CompositeRouteLocator,RouteDefinitionRouteLocator,这里以RouteDefinitionRouteLocator为例进行分析,代码如下:

kotlin 复制代码
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
	..........省略部分代码..........

	private final RouteDefinitionLocator routeDefinitionLocator;

	private final ConfigurationService configurationService;

	private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();

	private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();

	private final GatewayProperties gatewayProperties;
	
	public Flux<Route> getRoutes() {
		//将RouteDefinition集合转换为Route实例集合
		Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute);

		return routes.map(route -> {			
			return route;
		});
	}
}

getRoutes中this.routeDefinitionLocator.getRouteDefinitions获取所有的RouteDefinition,这个在上面已经分析过了,接下来分析下convertToRoute是如何将RouteDefinition转换为Route实例的:

scss 复制代码
private Route convertToRoute(RouteDefinition routeDefinition) {
	AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
	List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
	//使用构造器AsyncBuilder构造Route实例
	return Route.async(routeDefinition).asyncPredicate(predicate)
			.replaceFilters(gatewayFilters).build();
}

public static AsyncBuilder async(RouteDefinition routeDefinition) {	
	return new AsyncBuilder().id(routeDefinition.getId())
			.uri(routeDefinition.getUri())
			.order(routeDefinition.getOrder())
			.metadata(routeDefinition.getMetadata());	
}

public Route build() {
	return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters, this.metadata);
}

这里使用了构造器AsyncBuilder来构造Route实例,Route.async首先从routeDefinition读取信息到构造器,然后设置构造器的Predicate和Filter,最后构造Route实例。

六、根据Http请求匹配Route:

1、流程:

SpringCloud Gateway依赖于框架Spring WebFlux,因此他的总入口不是DispatcherServlet而是DispatcherHandler,入口方法在handle中:

scss 复制代码
public Mono<Void> handle(ServerWebExchange exchange) {	
	return Flux.fromIterable(this.handlerMappings)
			//匹配到Route
			.concatMap(mapping -> mapping.getHandler(exchange))
			.next()
			.switchIfEmpty(createNotFoundError())
			//通过FilteringWebHandler执行过滤器链和执行业务代码
			.flatMap(handler -> invokeHandler(exchange, handler))
			//处理返回结果数据
			.flatMap(result -> handleResult(exchange, result));
}

a、首先使用HandlerMapping从http请求匹配到Route,并返回FilteringWebHandler对象(mapping.getHandler(exchange));

b、接着通过FilteringWebHandler执行过滤器链和执行业务代码(invokeHandler(exchange, handler));

c、处理返回结果数据(handleResult(exchange, result));

2、HandlerMapping的初始化:

在DispatcherHandler的构造方法中从Spring容器过滤出所有实现了HandlerMapping接口的bean。

ini 复制代码
public class DispatcherHandler implements WebHandler, ApplicationContextAware {

	private List<HandlerMapping> handlerMappings;

	private List<HandlerAdapter> handlerAdapters;

	private List<HandlerResultHandler> resultHandlers;
	
	public DispatcherHandler(ApplicationContext applicationContext) {
		initStrategies(applicationContext);
	}
	
	protected void initStrategies(ApplicationContext context) {
		Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		this.handlerMappings = Collections.unmodifiableList(mappings);

		Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
		this.handlerAdapters = new ArrayList<>(adapterBeans.values());	

		Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);
		this.resultHandlers = new ArrayList<>(beans.values());		
	}	
	................
}

3、HandlerMapping匹配Route

HandlerMapping接口用于从http请求信息匹配到对应的Route,这个接只定义了一个方法getHandler,代码如下:

csharp 复制代码
public interface HandlerMapping {
	Mono<Object> getHandler(ServerWebExchange exchange);
}

HandlerMapping接口的实现类有很多,我们这里就分析下RoutePredicateHandlerMapping实现类是怎么匹配的流程:

scss 复制代码
public Mono<Object> getHandler(ServerWebExchange exchange) {
	return getHandlerInternal(exchange).map(handler -> {		
		return handler;
	});
}

protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {	
	//lookupRoute依据exchange匹配Route
	return lookupRoute(exchange)			
			.flatMap((Function<Route, Mono<?>>) r -> {				
				exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
				//返回FilteringWebHandler对象
				return Mono.just(webHandler);
			});
}

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
	//routeLocator.getRoutes()返回所有的Route实例
	return this.routeLocator.getRoutes()			
			.concatMap(route -> Mono.just(route).filterWhen(r -> {				
				exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
				//过滤出断言为true的route
				return r.getPredicate().apply(exchange);
			}));	
}

this.routeLocator.getRoutes返回所有的Route实例,这在上面已经分析过了。接下来遍历所有的Route,r.getPredicate().apply(exchange)对Route的断言进行判断,过滤出返回true的Route,至此匹配Route路由就完成。

getHandlerInternal最后返回的webHandler对象,从定义可以看到这个 webHandler是FilteringWebHandler类型的,也就是后面将交由他来执行过滤器链。

七、执行过滤器链和业务代码:

1、适配器HandlerAdapter:

由于HandlerMapping不止RoutePredicateHandlerMapping一种实现,还有其他实现,在RoutePredicateHandlerMapping实现中返回的是FilteringWebHandler(带过滤器链的webHandler),而其他类型中有的是返回WebSocketHandler,有的返回的是HandlerMethod(不带过滤器链的,直接执行业务代码)等等,要以一种统一的方式调用的话,就需要封装一个适配器接口HandlerAdapter:

java 复制代码
public interface HandlerAdapter {
	boolean supports(Object handler);
	
	Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler);
}

supports用于判断持哪种类型的Handler,handle方法用于具体handler的调用。

执行业务代码的invokeHandler就是遍历所有的适配器HandlerAdapter,判断哪种适配器支持WebHandler则进行调用:

kotlin 复制代码
private Mono<HandlerResult> 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));
}

支持WebHandler的适配器是SimpleHandlerAdapter:

typescript 复制代码
public class SimpleHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		return WebHandler.class.isAssignableFrom(handler.getClass());
	}

	@Override
	public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
		WebHandler webHandler = (WebHandler) handler;
		//这里会进入FilteringWebHandler.handle的调用
		Mono<Void> mono = webHandler.handle(exchange);
		return mono.then(Mono.empty());
	}
}

2、FilteringWebHandler的调用:

FilteringWebHandler首先加载全局过滤器:

java 复制代码
public class FilteringWebHandler implements WebHandler {

	private final List<GatewayFilter> globalFilters;

	public FilteringWebHandler(List<GlobalFilter> globalFilters) {
		this.globalFilters = loadFilters(globalFilters);
	}

	private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
		return filters.stream().map(filter -> {
			//利用适配器将GlobalFilter和GatewayFilter放到一个列表中
			GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);			
			return gatewayFilter;
		}).collect(Collectors.toList());
	}
	..............
}

private static class GatewayFilterAdapter implements GatewayFilter {
	private final GlobalFilter delegate;

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

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

全局过滤器GlobalFilter和Route过滤器GatewayFilter不是一个接口,不能放到一个集合中进行统一调用,因此定义了一个适配器GatewayFilterAdapter继承自GatewayFilter,这样两种过滤器就可以放到一个集合中统一调用了。

FilteringWebHandler的调用:

ini 复制代码
public Mono<Void> handle(ServerWebExchange exchange) {
	Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
	List<GatewayFilter> gatewayFilters = route.getFilters();

	//合并全局过滤器和Route过滤器
	List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
	combined.addAll(gatewayFilters);	

	//交由DefaultGatewayFilterChain执行
	return new DefaultGatewayFilterChain(combined).filter(exchange);
}

首先将全局过滤器和Route过滤器合并到一个集合,然后交由DefaultGatewayFilterChain执行:

kotlin 复制代码
private static class DefaultGatewayFilterChain implements GatewayFilterChain {
	//当前执行的过滤器的游标
	private final int index;
	//过滤器集合
	private final List<GatewayFilter> filters;

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

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

	public List<GatewayFilter> getFilters() {
		return filters;
	}

	@Override
	public Mono<Void> 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
			}
		});
	}
}

DefaultGatewayFilterChain是一个过滤器链,里面保存了一个过滤器集合和当前执行的游标,每次执行filter时,根据index取出过滤器执行,然后游标加一,将加一后的游标重新构造DefaultGatewayFilterChain去执行,直至所有的过滤器执行完成,最后返回Mono.empty()表示完成。

不对,所有过滤器执行完后不是应该再去执行业务代码吗,怎么返回Mono.empty()了,这样业务代码是不是就没地方执行了。断点调试看看过滤器链的数据,其中有过滤器NettyRoutingFilter和NettyWriteResponseFilter,NettyRoutingFilter负责发送请求到route目标地址服务执行,NettyWriteResponseFilter则负责将目标服务执行的结果输出到客户端。下面分别看看这两个过滤器。

3、请求业务代码和将执行结果写回客户端:

先看看NettyRoutingFilter的代码:

java 复制代码
public class NettyRoutingFilter implements GlobalFilter, Ordered {
	private final HttpClient httpClient;	

	private final HttpClientProperties properties;
	
	@Override
	public int getOrder() {
		//排序在过滤器链集合最后
		return Ordered.LOWEST_PRECEDENCE;
	}

	@Override	
	public Mono<Void> 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();		
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);

		//getHttpClient返回的就是httpClient,这里将请求发送给后端微服务执行
		Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange)
				.request(method).uri(url).send((req, nettyOutbound) -> {					
					return nettyOutbound.send(request.getBody().map(this::getByteBuf));
				});	
		//交给过滤器链的下一个过滤器执行
		return responseFlux.then(chain.filter(exchange));
	}
}

getOrder方法可以看到这个过滤器是排在过滤器链的最后面。filter方法首先获取业务服务真正的请求requestUrl,再用getHttpClient(route, exchange)获取到httpClient对象,接着调用httpClient向业务服务发送请求,将返回结果交由过滤器链的下一个过滤器来执行。

再看看NettyWriteResponseFilter的代码:

java 复制代码
public class NettyWriteResponseFilter implements GlobalFilter, Ordered {	
	@Override
	public int getOrder() {
		//在过滤器链中的位置还是比较靠前的
		return -1;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {	
		//chain.filter(exchange).then表示先交由过滤器链执行后面的过滤器,等后面的执行完成后再执行then后面的代码,相当于After Filter的效果
		return chain.filter(exchange)				
				.then(Mono.defer(() -> {
					//获取Connection
					Connection connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR);
					if (connection == null) {
						return Mono.empty();
					}
					
					//获取ServerHttpResponse
					ServerHttpResponse response = exchange.getResponse();
					//获取数据并转换数据
					final Flux<DataBuffer> body = connection
							.inbound()
							.receive()
							.retain()
							.map(byteBuf -> wrap(byteBuf, response));

					MediaType contentType = response.getHeaders().getContentType();	
					//writeAndFlushWith或writeWith都是将返回数据写回客户端
					return (isStreamingMediaType(contentType)
							? response.writeAndFlushWith(body.map(Flux::just))
							: response.writeWith(body));
				}));	
	}
}

getOrder的代码可以看到这个过滤器在过滤器链比较靠前的位置。filter方法,chain.filter(exchange).then表示先交由过滤器链执行后面的过滤器,等后面的执行完成后再执行then后面的代码,相当于After Filter的效果。then后面的代码首先获取Connection和ServerHttpResponse,接着获取返回业务请求返回数据并转换后的body,最后通过/writeAndFlushWith或writeWith都是将返回数据写回客户端。

相关推荐
掘金码甲哥10 分钟前
调试grpc的哼哈二将,你值得拥有
后端
陈小桔23 分钟前
idea中重新加载所有maven项目失败,但maven compile成功
java·maven
小学鸡!23 分钟前
Spring Boot实现日志链路追踪
java·spring boot·后端
xiaogg367835 分钟前
阿里云k8s1.33部署yaml和dockerfile配置文件
java·linux·kubernetes
逆光的July1 小时前
Hikari连接池
java
微风粼粼1 小时前
eclipse 导入javaweb项目,以及配置教程(傻瓜式教学)
java·ide·eclipse
番茄Salad1 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud
天若有情6731 小时前
Spring MVC文件上传与下载全面详解:从原理到实战
java·spring·mvc·springmvc·javaee·multipart
祈祷苍天赐我java之术1 小时前
Redis 数据类型与使用场景
java·开发语言·前端·redis·分布式·spring·bootstrap
用户21411832636022 小时前
OpenSpec 实战:用规范驱动开发破解 AI 编程协作难题
后端