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