目录

Gateway初始化

启用Gateway

GatewayClassPathWarningAutoConfiguration

GatewayLoadBalancerClientAutoConfiguration

GatewayAutoConfiguration

网关的开启和关闭

GlobalFilters

RoutePredicateFactory

 NettyConfiguration

核心组件构建

组件

Route

AsyncPredicate

GatewayFilter

Route构建

外部化配置

编程方式

构建原理

GatewayProperties

RouteDefinition

 FilterDefinition

PredicateDefinition

RoutePredicateFactory

GatewayFilterFactory

RouteLocator

RouteDefinitionRouteLocator


本文章源码为2.2.2-release,github:https://github.com/spring-cloud/spring-cloud-gateway/tree/v2.2.2.RELEASE

Gateway初始化

启用Gateway

官方示例中,启用Gateway,使用了@EnableAutoConfiguration注解。

@EnableAutoConfiguration
@Import(AdditionalRoutes.class)
public class GatewaySampleApplication {
......
}

@EnableAutoConfiguration注解会引入:

这些自动配置类都放在org.springframework.cloud.gateway.config下。在加载GatewayAutoConfiguration之前,会加载一些配置,通过@AutoConfigureAfter,@AutoConfigureBefore注解来控制加载顺序。

GatewayClassPathWarningAutoConfiguration

用于检查项目是否正确导入 spring-boot-starter-webflux 依赖,而不是错误导入 spring-boot-starter-web 依赖。

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(GatewayAutoConfiguration.class)
public class GatewayClassPathWarningAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
	protected static class SpringMvcFoundOnClasspathConfiguration {
		public SpringMvcFoundOnClasspathConfiguration() {
			log.warn(BORDER
					+ "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. "
					+ "Please remove spring-boot-starter-web dependency." + BORDER);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.web.reactive.DispatcherHandler")
	protected static class WebfluxMissingFromClasspathConfiguration {
		public WebfluxMissingFromClasspathConfiguration() {
			log.warn(BORDER + "Spring Webflux is missing from the classpath, "
					+ "which is required for Spring Cloud Gateway at this time. "
					+ "Please add spring-boot-starter-webflux dependency." + BORDER);
		}

	}

}

GatewayLoadBalancerClientAutoConfiguration

初始化 LoadBalancerClientFilter,可以使用LoadBalancerProperties加载配置信息。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ LoadBalancerClient.class, RibbonAutoConfiguration.class,
		DispatcherHandler.class })
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
public class GatewayLoadBalancerClientAutoConfiguration {

	@Bean
	@ConditionalOnBean(LoadBalancerClient.class)
	@ConditionalOnMissingBean({ LoadBalancerClientFilter.class,
			ReactiveLoadBalancerClientFilter.class })
	public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client,
			LoadBalancerProperties properties) {
		return new LoadBalancerClientFilter(client, properties);
	}

}

配置:spring.cloud.gateway.loadbalancer

@ConfigurationProperties("spring.cloud.gateway.loadbalancer")
public class LoadBalancerProperties {
	private boolean use404;
	public boolean isUse404() {
		return use404;
	}
	public void setUse404(boolean use404) {
		this.use404 = use404;
	}
}

GatewayAutoConfiguration

GatewayAutoConfiguration是Gateway的核心配置类。仅当时reactive类型 服务时才加载。

会初始化一些组件:

  • GlobalFilters
  • RoutePredicateFactory
  • RouteDefinitionLocator
  • NettyConfiguration
  • FilteringWebHandler
  • GatewayProperties
  • PrefixPathGatewayFilterFactory
  • RouteDefinitionLocator
  • RouteLocator
  • RoutePredicateHandlerMapping
  • GatewayWebfluxEndpoint

网关的开启和关闭

从 GatewayAutoConfiguration 上的注解 @ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true) ,我们可以看出 :

  • 通过 spring.cloud.gateway.enabled 配置网关的开启与关闭。
  • matchIfMissing = true   =>   网关默认开启

GlobalFilters

RoutePredicateFactory

 NettyConfiguration

核心组件构建

组件

Route

Route 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。

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;
	public int getOrder() {
		return order;
	}
}

Route 主要定义了如下几个部分:

① id,标识符,区别于其他 Route。

② destination uri,路由指向的目的地 uri,即客户端请求最终被转发的目的地。

③ order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。

④ predicate,谓语,表示匹配该 Route 的前置条件,即满足相应的条件才会被路由到目的地 uri。

⑤ gateway filters,过滤器用于处理切面逻辑,如路由转发前修改请求头等。

AsyncPredicate

Predicate 即 Route 中所定义的部分,用于条件匹配,请参考 Java 8 提供的 Predicate 和 Function。

public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {

	default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
		return new AndAsyncPredicate<>(this, other);
	}

	default AsyncPredicate<T> negate() {
		return new NegateAsyncPredicate<>(this);
	}

	default AsyncPredicate<T> or(AsyncPredicate<? super T> other) {
		return new OrAsyncPredicate<>(this, other);
	}

	static AsyncPredicate<ServerWebExchange> from(
			Predicate<? super ServerWebExchange> predicate) {
		return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
	}
}

 

AsyncPredicate 定义了 3 种逻辑操作方法:

① and ,与操作,即两个 Predicate 组成一个,需要同时满足。

② negate,取反操作,即对 Predicate 匹配结果取反。

③ or,或操作,即两个 Predicate 组成一个,只需满足其一。

GatewayFilter

很多框架都有 Filter 的设计,用于实现可扩展的切面逻辑。

public interface GatewayFilter extends ShortcutConfigurable {

	/**
	 * Name key.
	 */
	String NAME_KEY = "name";

	/**
	 * Value key.
	 */
	String VALUE_KEY = "value";

	/**
	 * 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);

}

Filter 最终是通过 filter chain 来形成链式调用的,每个 filter 处理完 pre filter 逻辑后委派给 filter chain,filter chain 再委派给下一下 filter。

public interface GatewayFilterChain {
	Mono<Void> filter(ServerWebExchange exchange);
}

Route构建

外部化配置

参考:Spring Cloud Gateway介绍(一)Spring Cloud Gateway介绍(二)

编程方式

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { // ①
    return builder.routes() // ②
            .route(r -> r.host("**.abc.org").and().path("/image/png") // ③
                .filters(f ->
                        f.addResponseHeader("X-TestHeader", "foobar")) // ④
                .uri("http://httpbin.org:80") // ⑤
            )
            .build();
}

RouteLocatorBuilder 在 spring-cloud-starter-gateway 模块自动装配类中已经声明,可直接使用。RouteLocator 封装了对 Route 获取的定义,可简单理解成工厂模式。

RouteLocatorBuilder 可以构建多个路由信息。

构建原理

GatewayProperties

GatewayProperties 是 Spring cloud gateway 模块提供的外部化配置类。

@ConfigurationProperties("spring.cloud.gateway") // ①
@Validated
public class GatewayProperties {

    /**
     * List of Routes
     */
    @NotNull
    @Valid
    private List<RouteDefinition> routes = new ArrayList<>(); // ②

    /**
     * List of filter definitions that are applied to every route.
     */
    private List<FilterDefinition> defaultFilters = new ArrayList<>(); // ③
}

RouteDefinition

该组件用来对 Route 信息进行定义,最终会被 RouteLocator 解析成 Route。

 FilterDefinition

用来定义Filter。

@Validated
public class FilterDefinition {

	@NotNull
	private String name;

	private Map<String, String> args = new LinkedHashMap<>();
}

 通过构造函数来设置参数。

	public FilterDefinition(String text) {
		int eqIdx = text.indexOf('=');
		if (eqIdx <= 0) {
			setName(text);
			return;
		}
		setName(text.substring(0, eqIdx));

		String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");

		for (int i = 0; i < args.length; i++) {
			this.args.put(NameUtils.generateName(i), args[i]);
		}
	}

PredicateDefinition

用于定义 Predicate。

RoutePredicateFactory

RoutePredicateFactory 是所有 predicate factory 的顶级接口,职责就是生产 Predicate。

创建一个用于配置用途的对象(config),以其作为参数应用到 apply方法上来生产一个 Predicate 对象,再将 Predicate 对象包装成 AsyncPredicate。

public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {

	/**
	 * Pattern key.
	 */
	String PATTERN_KEY = "pattern";

	// useful for javadsl
	default Predicate<ServerWebExchange> apply(Consumer<C> consumer) {
		C config = newConfig();
		consumer.accept(config);
		beforeApply(config);
		return apply(config);
	}

}

GatewayFilterFactory

GatewayFilterFactory 职责就是生产 GatewayFilter。

@FunctionalInterface
public interface GatewayFilterFactory<C> extends ShortcutConfigurable,
    Configurable<C> { // ①
    String NAME_KEY = "name";
    String VALUE_KEY = "value";

    GatewayFilter apply(C config); // ②
}

RouteLocator

 Route 的定位器或者说探测器,是用来获取 Route 信息的。

public interface RouteLocator {
    Flux<Route> getRoutes(); // ①
}

RouteDefinitionRouteLocator

RouteLocator 最主要的实现类,用于将 RouteDefinition 转换成 Route。

public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
    private final RouteDefinitionLocator routeDefinitionLocator;
    private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
    private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>(); private final GatewayProperties gatewayProperties;
    private final SpelExpressionParser parser = new SpelExpressionParser();
    private BeanFactory beanFactory;
    private ApplicationEventPublisher publisher;

}

构造函数

 public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,// 
        List<RoutePredicateFactory> predicates, // 
        List<GatewayFilterFactory> gatewayFilterFactories, // 
        GatewayProperties gatewayProperties) { // 
        this.routeDefinitionLocator = routeDefinitionLocator;
        initFactories(predicates);
        gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));
        this.gatewayProperties = gatewayProperties;
}

构造函数依赖 4 个对象,分别是:

① RouteDefinition Locator,一个 RouteDefinitionLocator 对象。

② predicates factories,Predicate 工厂列表,会被映射成 key 为 name, value 为 factory 的 Map。可以猜想出 gateway 是如何根据 PredicateDefinition 中定义的 name 来匹配到相对应的 factory 了。

③ filter factories,Gateway Filter 工厂列表,同样会被映射成 key 为 name, value 为 factory 的 Map。

④ gateway properties,外部化配置类。

疑问:该类依赖 GatewayProperties 对象,后者已经携带了 List 结构的 RouteDefinition,那为什么还要依赖 RouteDefinitionLocator 来提供 RouteDefinition?

  1. 这里并不会直接使用到 GatewayProperties 类中的 RouteDefinition,仅是用到其定义的 default filters,这会应用到每一个 Route 上。
  2. 最终传入的 RouteDefinitionLocator 实现上是 CompositeRouteDefinitionLocator 的实例,它组合了 GatewayProperties 中所定义的 routes。

getRoutes()

@Override
	public Flux<Route> getRoutes() {
		Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()  //获取 RouteDefinition
				.map(this::convertToRoute);  //转换成Route

......

		return routes.map(route -> {
			if (logger.isDebugEnabled()) {
				logger.debug("RouteDefinition matched: " + route.getId());
			}
			return route;
		});
	}
//将 RouteDefinition 转换成 Route。
	private Route convertToRoute(RouteDefinition routeDefinition) {
        //将 PredicateDefinition 转换成 AsyncPredicate。
		AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
        //将 FilterDefinition 转换成 GatewayFilter。
		List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
        //生成 Route 对象。
		return Route.async(routeDefinition).asyncPredicate(predicate)
				.replaceFilters(gatewayFilters).build();
	}
FilterDefinition 转换成 GatewayFilter
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
		List<GatewayFilter> filters = new ArrayList<>();

		// TODO: support option to apply defaults after route specific filters?
        //获取gateway配置的默认的filters
		if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
			filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
					this.gatewayProperties.getDefaultFilters()));
		}
        //获取每个route配置的 gatewayFilters
		if (!routeDefinition.getFilters().isEmpty()) {
			filters.addAll(loadGatewayFilters(routeDefinition.getId(),
					routeDefinition.getFilters()));
		}
        //排序
		AnnotationAwareOrderComparator.sort(filters);
		return filters;
	}

PredicateDefinition 转换成 AsyncPredicate

	private AsyncPredicate<ServerWebExchange> combinePredicates(
			RouteDefinition routeDefinition) {
        //获取配置的route的predicate。
		List<PredicateDefinition> predicates = routeDefinition.getPredicates();
        //将列表中第一个 PredicateDefinition 转换成 AsyncPredicate。
		AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
				predicates.get(0));
        //循环调用,将列表中每一个 PredicateDefinition 都转换成 AsyncPredicate。
		for (PredicateDefinition andPredicate : predicates.subList(1,
				predicates.size())) {
			AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
					andPredicate);
            //应用and操作,将所有的 AsyncPredicate 组合成一个 AsyncPredicate 对象。
			predicate = predicate.and(found);
		}

		return predicate;
	}

lookup

private AsyncPredicate<ServerWebExchange> lookup(
    RouteDefinition route, PredicateDefinition predicate) {
//根据 predicate 名称获取对应的 predicate factory。
    RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
    if (factory == null) {
        throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name             " + predicate.getName());
    }
//获取 PredicateDefinition 中的 Map 类型参数,key 是固定字符串_genkey_ + 数字拼接而成。
    Map<String, String> args = predicate.getArgs();// 
    if (logger.isDebugEnabled()) {
        logger.debug("RouteDefinition " + route.getId() + " applying "
                     + args + " to " + predicate.getName());
    }
//对上 步获得的参数作进一步转换,key为 config 类(工厂类中通过范型指定)的属性名称。
    Map<String, Object> properties = factory.shortcutType().normalize(
        args, factory, this.parser, this.beanFactory);
//调用 factory 的 newConfig 方法创建一个 config 类对象。
    Object config = factory.newConfig();
//将上步中产生的参数绑定到 config 对象上。
    ConfigurationUtils.bind(config, properties,
                            factory.shortcutFieldPrefix(), predicate.getName(),                                     validator); 

    if (this.publisher != null) {
        this.publisher.publishEvent(
            new PredicateArgsEvent(this, route.getId(), properties));
    }
//将 cofing 作参数代入,调用 factory 的 applyAsync 方法创建 AsyncPredicate 对象。
    return factory.applyAsync(config); 
}

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐