Spring Cloud Gateway 源碼解析(1) —— 基礎

本文章源碼爲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); 
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章