目錄
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?
- 這裡并不會直接使用到 GatewayProperties 類中的 RouteDefinition,僅是用到其定義的 default filters,這會應用到每一個 Route 上。
- 最終傳入的 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);
}