天天看點

Spring Cloud Gateway 源碼解析(1) —— 基礎Gateway初始化核心元件建構

目錄

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注解會引入:

Spring Cloud Gateway 源碼解析(1) —— 基礎Gateway初始化核心元件建構

這些自動配置類都放在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

Spring Cloud Gateway 源碼解析(1) —— 基礎Gateway初始化核心元件建構

RoutePredicateFactory

Spring Cloud Gateway 源碼解析(1) —— 基礎Gateway初始化核心元件建構

 NettyConfiguration

Spring Cloud Gateway 源碼解析(1) —— 基礎Gateway初始化核心元件建構

核心元件建構

元件

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); 
}
           

繼續閱讀