1. 概述
本文主要對 DiscoveryClientRouteDefinitionLocator 的源碼實作。
DiscoveryClientRouteDefinitionLocator 通過調用
org.springframework.cloud.client.discovery.DiscoveryClient
擷取注冊在注冊中心的服務清單,生成對應的 RouteDefinition 數組。
2. 環境搭建
在解析源碼之前,我們先以 Eureka 為注冊中心,講解下如何配置 DiscoveryClientRouteDefinitionLocator 。
第一步,以
spring-cloud-gateway-sample
項目為基礎,在
pom.xml
檔案添加依賴庫。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>2.0.0.M1</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
- 注意,需要排除
的依賴,避免和 Spring Cloud Gateway 依賴的spring-boot-starter-web
沖突。spring-boot-starter-webflux
第二步,在
application.yml
添加 Eureka 相關配置 。
spring:
application:
name: juejin-gateway
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 30
client:
serviceUrl:
defaultZone: http://eureka.didispace.com/eureka/
- 如果你“懶”的啟動 Eureka ,推薦使用 《程式猿DD - 公益 - EUREKA SERVER》 。感謝 1024 。
第三步,在
org.springframework.cloud.gateway.sample.GatewaySampleApplication
類裡,添加 RouteDefinitionLocator Bean 配置
@EnableDiscoveryClient // {@link DiscoveryClientRouteDefinitionLocator}
public class GatewaySampleApplication {
// ... 省略其他代碼
@Bean
public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
}
}
第四步,啟動一個注冊在 Eureka 的應用服務。機智如你,這裡我就不啰嗦落。
第五步,在 DiscoveryClientRouteDefinitionLocator 的
#getRouteDefinitions()
方法打斷點,調試啟動
spring-cloud-gateway-sample
。小功告成。撒花~
3. DiscoveryClientRouteDefinitionLocator
org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator
,通過調用 DiscoveryClient 擷取注冊在注冊中心的服務清單,生成對應的 RouteDefinition 數組。
代碼如下 :
1: public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
2:
3: private final DiscoveryClient discoveryClient;
4: private final String routeIdPrefix;
5:
6: public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
7: this.discoveryClient = discoveryClient;
8: this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";
9: }
10:
11: @Override
12: public Flux<RouteDefinition> getRouteDefinitions() {
13: return Flux.fromIterable(discoveryClient.getServices())
14: .map(serviceId -> {
15: RouteDefinition routeDefinition = new RouteDefinition();
16: // 設定 ID
17: routeDefinition.setId(this.routeIdPrefix + serviceId);
18: // 設定 URI
19: routeDefinition.setUri(URI.create("lb://" + serviceId));
20:
21: // add a predicate that matches the url at /serviceId
22: /*PredicateDefinition barePredicate = new PredicateDefinition();
23: barePredicate.setName(normalizePredicateName(PathRoutePredicateFactory.class));
24: barePredicate.addArg(PATTERN_KEY, "/" + serviceId);
25: routeDefinition.getPredicates().add(barePredicate);*/
26:
27: // 添加 Path 比對斷言
28: // add a predicate that matches the url at /serviceId/**
29: PredicateDefinition subPredicate = new PredicateDefinition();
30: subPredicate.setName(normalizePredicateName(PathRoutePredicateFactory.class));
31: subPredicate.addArg(PATTERN_KEY, "/" + serviceId + "/**");
32: routeDefinition.getPredicates().add(subPredicate);
33:
34: //TODO: support for other default predicates
35:
36: // 添加 Path 重寫過濾器
37: // add a filter that removes /serviceId by default
38: FilterDefinition filter = new FilterDefinition();
39: filter.setName(normalizeFilterName(RewritePathGatewayFilterFactory.class));
40: String regex = "/" + serviceId + "/(?<remaining>.*)";
41: String replacement = "/${remaining}";
42: filter.addArg(REGEXP_KEY, regex);
43: filter.addArg(REPLACEMENT_KEY, replacement);
44: routeDefinition.getFilters().add(filter);
45:
46: //TODO: support for default filters
47:
48: return routeDefinition;
49: });
50: }
51: }
-
屬性,注冊發現用戶端,用于向注冊中心發起請求。discoveryClient
-
屬性,路由配置編号字首,以 DiscoveryClient 類名 +routeIdPrefix
。_
- 第 13 行 :調用
擷取注冊在注冊中心的服務清單。discoveryClient
- 第 14 行 :周遊服務清單,生成對應的 RouteDefinition 數組。
- 第 16 行 :設定
。RouteDefinition.id
- 第 18 行 :設定
,格式為RouteDefinition.uri
。在 LoadBalancerClientFilter 會根據lb://${serviceId}
字首過濾處理,負載均衡,選擇最終調用的服務位址,在 《Spring-Cloud-Gateway 源碼解析 —— 過濾器 (4.4) 之 LoadBalancerClientFilter 負載均衡》 詳細解析。lb://
- 第 27 至 32 行 :使用 PathRoutePredicateFactory 建立 Path 比對斷言。
- 例如服務的
,通過網關serviceId = spring.application.name = juejin-sample
通路服務http://${gateway}/${serviceId}/some_api
。http://some_api
- PathRoutePredicateFactory 在 《Spring-Cloud-Gateway 源碼解析 —— 處理器 (3.1) 之 RoutePredicateFactory 路由謂語工廠》「10. PathRoutePredicateFactory」 有詳細解析。
- 例如服務的
- 第 36 至 44 行 :使用 RewritePathGatewayFilterFactory 建立重寫網關過濾器,用于移除請求路徑裡的
。如果不移除,最終請求不到服務。RewritePathGatewayFilterFactory 在 《Spring-Cloud-Gateway 源碼解析 —— 過濾器 (4.2) 之 GatewayFilterFactory 過濾器工廠》「4.1 RewritePathGatewayFilterFactory」 有詳細解析。/${serviceId}
- 第 48 行 :傳回路由定義 RouteDefinition 。舉個 RouteDefinition 例子 :
Spring-Cloud-Gateway 源碼解析 —— 路由(1.4)之 DiscoveryClientRouteDefinitionLocator 注冊中心1. 概述2. 環境搭建3. DiscoveryClientRouteDefinitionLocator4. 高能
4. 高能
本小節建議閱讀完 《Spring-Cloud-Gateway 源碼解析 —— 路由(2.1)之 RouteLocator 一覽》 再了解。
RoutePredicateHandlerMapping 使用 CachingRouteLocator 來擷取 Route 資訊。在 Spring Cloud Gateway 啟動後,如果有新加入的服務,則需要重新整理 CachingRouteLocator 緩存。
這裡有一點需要注意下 :新加入的服務,指的是新的
serviceId
,而不是原有服務新增的執行個體。
個人建議,寫一個定時任務,間隔調用 DiscoveryClient 擷取服務清單,若發現變化,重新整理 CachingRouteLocator 緩存。