在Spring-Cloud-Gateway之請求處理流程中最終網關是將請求交給過濾器鏈表進行處理。html
核心接口:GatewayFilter,GlobalFilter,GatewayFilterChain。spring
查看總體類圖編程
當使用微服務構建整個 API 服務時,通常有許多不一樣的應用在運行,如上圖所示的mst-user-service
、mst-good-service
和mst-order-service
,這些服務都須要對客戶端的請求的進行 Authentication。最簡單粗暴的方法就是像上圖同樣,爲每一個微服務應用都實現一套用於校驗的過濾器或攔截器。安全
經過前置的網關服務來完成這些非業務性質的校驗。app
Spring Cloud Gateway 的 Filter 的生命週期有兩個:「pre」 和 「post」。負載均衡
「pre」和 「post」 分別會在請求被執行前調用和被執行後調用,和 Zuul Filter 或 Spring Interceptor 中相關生命週期相似,但在形式上有些不同。ide
Zuul 的 Filter 是經過filterType()
方法來指定,一個 Filter 只能對應一種類型,要麼是 「pre」 要麼是「post」。Spring Interceptor 是經過重寫HandlerInterceptor
中的三個方法來實現的。而 Spring Cloud Gateway 基於 Project Reactor 和 WebFlux,採用響應式編程風格,打開它的 Filter 的接口GatewayFilter
你會發現它只有一個方法filter
。函數
/** * 網關過濾鏈表接口 * 用於過濾器的鏈式調用 */ public interface GatewayFilterChain { /** * 鏈表啓動調用入口方法*/ Mono<Void> filter(ServerWebExchange exchange); }
默認實現微服務
/** * 網關過濾的鏈表,用於過濾器的鏈式調用 * 過濾器鏈表接口的默認實現, * 包含2個構建函數: * 1.集合參數構建用於初始化吧構建鏈表 * 2. index,parent參數用於構建當前執行過濾對應的下次執行的鏈表 */ private static class DefaultGatewayFilterChain implements GatewayFilterChain { /** * 當前過濾執行過濾器在集合中索引 */ private final int index; /** * 過濾器集合 */ private final List<GatewayFilter> filters; public DefaultGatewayFilterChain(List<GatewayFilter> filters) { this.filters = filters; this.index = 0; } /** * 構建 * @param parent 上一個執行過濾器對應的FilterChain * @param index 當前要執行過濾器的索引 */ private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) { this.filters = parent.getFilters(); this.index = index; } public List<GatewayFilter> getFilters() { return filters; } /** * @param exchange the current server exchange * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange) { return Mono.defer(() -> { if (this.index < filters.size()) { //獲取當前索引的過濾器 GatewayFilter filter = filters.get(this.index); //構建當前索引的下一個過濾器的FilterChain DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1); //調用過濾器的filter方法執行過濾器 return filter.filter(exchange, chain); } else { //當前索引大於等於過濾集合大小,標識全部鏈表都已執行完畢,返回空 return Mono.empty(); // complete } }); } }
過濾器的GatewayFilterChain 執行順序post
/** * 網關路由過濾器, * Contract for interception-style, chained processing of Web requests that may * be used to implement cross-cutting, application-agnostic requirements such * as security, timeouts, and others. Specific to a Gateway * */ public interface GatewayFilter extends ShortcutConfigurable { String NAME_KEY = "name"; 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,執行當前過濾器,並在此方法中決定過濾器鏈表是否繼續往下執行。
1️⃣、OrderedGatewayFilter--排序
/** * 排序的網關路由過濾器,用於包裝真實的網關過濾器,已達到過濾器可排序 */ public class OrderedGatewayFilter implements GatewayFilter, Ordered { //目標過濾器 private final GatewayFilter delegate; //排序字段 private final int order; public OrderedGatewayFilter(GatewayFilter delegate, int order) { this.delegate = delegate; this.order = order; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return this.delegate.filter(exchange, chain); } }
OrderedGatewayFilter實現類主要目的是爲了將目標過濾器包裝成可排序的對象類型。是目標過濾器的包裝類
2️⃣、GatewayFilterAdapter
/** * 全局過濾器的包裝類,將全局路由包裝成統一的網關過濾器 */ private static class GatewayFilterAdapter implements GatewayFilter { /** * 全局過濾器 */ private final GlobalFilter delegate; public GatewayFilterAdapter(GlobalFilter delegate) { this.delegate = delegate; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return this.delegate.filter(exchange, chain); } }
GatewayFilterAdapter實現類主要目的是爲了將GlobalFilter過濾器包裝成GatewayFilter類型的對應。是GlobalFilter過濾器的包裝類
GlobalFilter 爲請求業務以及路由的URI轉換爲真實業務服務的請求地址的核心過濾器,不須要配置,模式系統初始化時加載,並做用在每一個路由上。
1️⃣、初始化加載,經過GatewayAutoConfiguration自動建立
//GatewayAutoConfiguration 類 /** * 全局過濾器,用戶經過HttpClient轉發請求 */ @Bean public NettyRoutingFilter routingFilter(HttpClient httpClient, ObjectProvider<List<HttpHeadersFilter>> headersFilters) { return new NettyRoutingFilter(httpClient, headersFilters); } /** * 全局的過濾器,用戶將HttpClient客戶端轉發請求的響應寫入到原始的請求響應中 */ @Bean public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) { return new NettyWriteResponseFilter(properties.getStreamingMediaTypes()); } //GatewayLoadBalancerClientAutoConfiguration 類 /** * 全局過濾器,用於在經過負載均衡客戶端選擇服務實例信息 */ @Bean @ConditionalOnBean(LoadBalancerClient.class) public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) { return new LoadBalancerClientFilter(client); }
2️⃣、GlobalFilter轉換成GatewayFilter,並做用於每一個路由上,在FilteringWebHandler實現
//FilteringWebHandler類 /** * 包裝加載全局的過濾器,將全局過濾器包裝成GatewayFilter */ private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) { return filters.stream() .map(filter -> { //將全部的全局過濾器包裝成網關過濾器 GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter); //判斷全局過濾器是否實現了可排序接口 if (filter instanceof Ordered) { int order = ((Ordered) filter).getOrder(); //包裝成可排序的網關過濾器 return new OrderedGatewayFilter(gatewayFilter, order); } return gatewayFilter; }).collect(Collectors.toList()); } @Override public Mono<Void> handle(ServerWebExchange exchange) { //獲取請求上下文設置的路由實例 Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); //獲取路由定義下的網關過濾器集合 List<GatewayFilter> gatewayFilters = route.getFilters(); //組合全局的過濾器與路由配置的過濾器 List<GatewayFilter> combined = new ArrayList<>(this.globalFilters); //添加路由配置過濾器到集合尾部 combined.addAll(gatewayFilters); //對過濾器進行排序 //TODO: needed or cached? AnnotationAwareOrderComparator.sort(combined); logger.debug("Sorted gatewayFilterFactories: "+ combined); //建立過濾器鏈表對其進行鏈式調用 return new DefaultGatewayFilterChain(combined).filter(exchange); }
Spring-Cloud-Gateway的過濾器接口分爲兩種:
1️⃣、加載GatewayFilter
在路由定位器中以及看到了經過路由定義轉換路由方法,其中包含了經過過濾器定義(FilterDefinition)轉換過濾器(GatewayFilter)的部分,在RouteDefinitionRouteLocator類中源碼以下:
/** * 加載過濾器,根據過濾器的定義加載 */ @SuppressWarnings("unchecked") private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) { //遍歷過濾器定義,將過濾器定義轉換成對應的過濾器 List<GatewayFilter> filters = filterDefinitions.stream() .map(definition -> { //經過過濾器定義名稱獲取過濾器建立工廠 GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName()); if (factory == null) { throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName()); } //獲取參數 Map<String, String> args = definition.getArgs(); if (logger.isDebugEnabled()) { logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName()); } //根據args組裝配置信息 Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory); //構建過濾器建立配置信息 Object configuration = factory.newConfig(); ConfigurationUtils.bind(configuration, properties, factory.shortcutFieldPrefix(), definition.getName(), validator); //經過過濾器工廠建立GatewayFilter GatewayFilter gatewayFilter = factory.apply(configuration); if (this.publisher != null) { //發佈事件 this.publisher.publishEvent(new FilterArgsEvent(this, id, properties)); } return gatewayFilter; }) .collect(Collectors.toList()); ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size()); //包裝過濾器使其全部過濾器繼承Ordered屬性,可進行排序 for (int i = 0; i < filters.size(); i++) { GatewayFilter gatewayFilter = filters.get(i); if (gatewayFilter instanceof Ordered) { ordered.add(gatewayFilter); } else { ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1)); } } return ordered; } /** * 獲取RouteDefinition中的過濾器集合 */ private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) { List<GatewayFilter> filters = new ArrayList<>(); //校驗gatewayProperties是否含義默認的過濾器集合 //TODO: support option to apply defaults after route specific filters? if (!this.gatewayProperties.getDefaultFilters().isEmpty()) { //加載全局配置的默認過濾器集合 filters.addAll(loadGatewayFilters("defaultFilters", this.gatewayProperties.getDefaultFilters())); } if (!routeDefinition.getFilters().isEmpty()) { //加載路由定義中的過濾器集合 filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters())); } //排序 AnnotationAwareOrderComparator.sort(filters); return filters; }
子類及其劃分
3️⃣、AddResponseHeaderGatewayFilterFactory 建立解析
/** * * 響應header添加數據過濾器 * 用戶在response header中添加配置數據 */ public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory { @Override public GatewayFilter apply(NameValueConfig config) { return (exchange, chain) -> { //獲取Response並將配置的數據添加到header中 exchange.getResponse().getHeaders().add(config.getName(), config.getValue()); return chain.filter(exchange); }; } }。
配置示例:
spring: cloud: gateway: default-filters: - AddResponseHeader=X-Response-Default-Foo, Default-Bar
4️⃣、所有配置
5.一、請求頭
spring:
cloud:
gateway:
routes:
- id: add_request_header_route uri: http://example.org filters: - AddRequestHeader=X-Request-Foo, Bar
名稱和值,這將爲全部匹配請求的下游請求標頭添加X-Request-Foo:Bar標頭。
移除請求頭
filters:
- RemoveRequestHeader=X-Request-Foo
5.二、請求參數
filters:
- AddRequestParameter=foo, bar
這會將foo = bar添加到下游請求的全部匹配請求的查詢字符串中。
5.三、添加響應頭
filters:
- AddResponseHeader=X-Response-Foo, Bar
這會將X-Response-Foo:Bar標頭添加到全部匹配請求的下游響應標頭中。
移除響應頭
filters:
- RemoveResponseHeader=X-Response-Foo
設置響應頭
filters:
- SetResponseHeader=X-Response-Foo, Bar
此GatewayFilter將替換具備給定名稱的全部標頭,而不是添加。
5.四、路徑前綴
filters:
- PrefixPath=/mypath
這將使/ mypath前綴爲全部匹配請求的路徑。因此對/ hello的請求會被髮送到/ mypath / hello。
5.五、原始主機頭
沒有參數,此過濾器設置路由過濾器將檢查的請求屬性,以肯定是否應發送原始主機頭,而不是http客戶端肯定的主機頭。
filters:
- PreserveHostHeader
5.六、重定向
filters:
- RedirectTo=302, http://acme.org
這將發送帶有Location:http://acme.org標頭的狀態302以執行重定向。
5.七、重寫路徑
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?<segment>.*), /$\{segment}
對於/ foo / bar的請求路徑,這將在發出下游請求以前將路徑設置爲/ bar。注意因爲YAML規範,$ \替換爲$。
5.八、保存Session
predicates:
- Path=/foo/**
filters:
- SaveSession
5.九、路徑模板
SetPath GatewayFilter Factory採用路徑模板參數。它提供了一種經過容許模板化路徑段來操做請求路徑的簡單方法。
predicates:
- Path=/foo/{segment} filters: - SetPath=/{segment}
對於/ foo / bar的請求路徑,這將在發出下游請求以前將路徑設置爲/ bar。
5.十、設置響應狀態
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route uri: http://example.org filters: - SetStatus=BAD_REQUEST - id: setstatusint_route uri: http://example.org filters: - SetStatus=401
5.十一、請求參數剝離
parts參數指示在將請求發送到下游以前從請求中剝離的路徑中的部分數。
predicates:
- Path=/name/**
filters:
- StripPrefix=2
當經過網關向/ name / bar / foo發出請求時,對nameservice的請求將相似於http:// nameservice / foo。
5.十二、重試
retries:重試:應該嘗試的重試次數
statuses:狀態:應該重試的HTTP狀態代碼,使用org.springframework.http.HttpStatus表示
methods:方法:應該重試的HTTP方法,使用org.springframework.http.HttpMethod表示
series:系列:要重試的狀態代碼系列,使用org.springframework.http.HttpStatus.Series表示
routes:
- id: retry_test uri: http://localhost:8080/flakey predicates: - Host=*.retry.com filters: - name: Retry args: retries: 3 statuses: BAD_GATEWAY
5.1三、Hystrix GatewayFilter Factory
5.1四、請求限速
RequestRateLimiter GatewayFilter Factory
5.1五、安全頭