目錄
一、簡介
二、RouteDefinitionLocator
1、RouteDefinition
三、RouteDefinitionRepository & InMemoryRouteDefinitionRepository
四、PropertiesRouteDefinitionLocator 基于配置屬性的路由定義定位器
五、DiscoveryClientRouteDefinitionLocator 基于服務發現的路由定義定位器
六、CachingRouteDefinitionLocator 基于緩存的路由定義定位器
七、CompositeRouteDefinitionLocator 組合路由定義定位器
Spring Cloud Gateway 學習專欄
1. Spring Cloud : Gateway 服務網關認識(一)
2. Spring Cloud :整合Gateway 學習 (二)
3. Spring Cloud:Gateway 路由定義定位器 RouteDefinitionLocator (三)
4. Spring Cloud : Gateway 網關過濾器 GatewayFilter(四)
5. Spring Cloud : Gateway 網關限流(五)
6. Spring Cloud:Gateway 內建 Sentinel (六)
7. Spring Cloud : Gateway Redis動态路由 (七)
8. Spring Cloud : Gateway 整合Swagger (八)
如果發現本文有錯誤的地方,請大家毫不吝啬,多多指教,歡迎大家評論,謝謝!
本文基于 spring-cloud-gateway 2.1.3.RELEASE
一、簡介
RouteDefinitionLocator 是路由定義定位器的頂級接口,它的主要作用就是讀取路由的配置資訊(org.springframework.cloud.gateway.route.RouteDefinition)。它有五種不同的實作類,如圖
二、RouteDefinitionLocator
org.springframework.cloud.gateway.route.RouteDefinitionLocator ,路由定義定位器接口,隻有一個方法,用來擷取路由定義清單的方法。
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}
通過 RouteDefinitionLocator 的類圖,可以看出該接口有多個實作類:
- PropertiesRouteDefinitionLocator:基于屬性配置
- DiscoveryClientRouteDefinitionLocator:基于服務發現
- CompositeRouteDefinitionLocator:組合方式
- CachingRouteDefinitionLocator:緩存方式
- 其中還有一個接口 RouteDefinitionRepository 繼承自RouteDefinitionLocator,用于對路由定義的操作(儲存、删除路由定義)
1、RouteDefinition
RouteDefinition 作為GatewayProperties中的屬性,在網關啟動的時候讀取配置檔案中的相關配置資訊
@Validated
public class RouteDefinition {
@NotEmpty
private String id = UUID.randomUUID().toString();
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList();
@Valid
private List<FilterDefinition> filters = new ArrayList();
@NotNull
private URI uri;
private int order = 0;
public RouteDefinition() {
}
public RouteDefinition(String text) {
int eqIdx = text.indexOf(61);
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse RouteDefinition text '" + text + "', must be of the form name=value");
} else {
this.setId(text.substring(0, eqIdx));
String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");
this.setUri(URI.create(args[0]));
for(int i = 1; i < args.length; ++i) {
this.predicates.add(new PredicateDefinition(args[i]));
}
}
}
----------------------省略----------------------------
}
在 RouteDefinition 中,主要有五個屬性:
- id:路由id,預設為uuid
- predicates:PredicateDefinition 路由斷言定義清單
- filters:FilterDefinition 過濾器定義清單
- uri:URI 轉發位址
- order:優先級
進入斷言和路由器屬性可以看到他們是一個 Map 資料結構,可以存放多個對應的 鍵值對數組
三、RouteDefinitionRepository & InMemoryRouteDefinitionRepository
RouteDefinitionRepository 接口中的方法用來對RouteDefinition進行增、删、查操作
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
}
//讀取路由定義資訊
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}
//對路由定會進行儲存和删除操作
public interface RouteDefinitionWriter {
Mono<Void> save(Mono<RouteDefinition> route);
Mono<Void> delete(Mono<String> routeId);
}
RouteDefinitionRepository 通過繼承自 RouteDefinitionLocator、 RouteDefinitionWriter,封裝了對路由定義資訊的擷取、增加、删除操作,在網關内置API端點接口時會用到這些操作。
InMemoryRouteDefinitionRepository 實作了 RouteDefinitionRepository 接口,基于記憶體的路由定義倉庫,同時也是唯一提供的實作類。我們可以根據需要自定義擴充,存放到其它的存儲媒體中。
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap());
public InMemoryRouteDefinitionRepository() {
}
//儲存路由定義到記憶體中
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap((r) -> {
this.routes.put(r.getId(), r);
return Mono.empty();
});
}
//根據路由id從記憶體中删除指定路由定義
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap((id) -> {
if (this.routes.containsKey(id)) {
this.routes.remove(id);
return Mono.empty();
} else {
return Mono.defer(() -> {
return Mono.error(new NotFoundException("RouteDefinition not found: " + routeId));
});
}
});
}
//擷取記憶體中路由定義清單
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.routes.values());
}
}
四、PropertiesRouteDefinitionLocator 基于配置屬性的路由定義定位器
從配置檔案 yaml或properties中讀取路由配置資訊,如代碼所示
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
private final GatewayProperties properties;
public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
this.properties = properties;
}
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.properties.getRoutes());
}
}
PropertiesRouteDefinitionLocator 通過構造函數傳入 GatewayProperties 對象,然後從該對象中讀取路由配置資訊
五、DiscoveryClientRouteDefinitionLocator 基于服務發現的路由定義定位器
該類通過服務發現元件從注冊中心擷取服務資訊,此時路由定義的源就是配置中心
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
//服務發現用戶端
private final DiscoveryClient discoveryClient;
//服務發現屬性
private final DiscoveryLocatorProperties properties;
//路由id字首
private final String routeIdPrefix;
------------------------------省略--------------------------------
}
//服務發現屬性對象
@ConfigurationProperties("spring.cloud.gateway.discovery.locator")
public class DiscoveryLocatorProperties {
// 開啟服務發現
private boolean enabled = false;
// 路由字首,預設為 discoveryClient. getClass(). getSimpleName() + "_".
private String routeIdPrefix;
// SpEL 表達式,判斷網關是否內建一個服務,預設為 true
private String includeExpression = "true";
// SpEL 表達式,為每個路由建立uri,預設為'lb://'+ serviceId
private String urlExpression = "'lb://'+ serviceId";
// 在 斷言 和 過濾器 中使用小寫 serviceId,預設為 false
private boolean lowerCaseServiceId = false;
//路由斷言定義清單
private List<PredicateDefinition> predicates = new ArrayList();
//過濾器定義清單
private List<FilterDefinition> filters = new ArrayList();
------------------------------省略--------------------------------
}
在 DiscoveryLocatorProperties 定義了以上屬性,要啟用基于服務發現的路由定義定位器就必須設定
spring.cloud.gateway.discovery.locator.enabled= true
includeExpression 屬性判斷網關是否內建一個服務,預設為true,根據 includeExpression 表達式,過濾不符合的 ServiceInstance。
DiscoveryClientRouteDefinitionLocator -> getRouteDefinitions()
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
//對 includeExpression 和 urlExpression 的 表達式 處理
SpelExpressionParser parser = new SpelExpressionParser();
Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
Expression urlExpr = parser.parseExpression(properties.getUrlExpression());
Predicate<ServiceInstance> includePredicate;
if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {
includePredicate = instance -> true;
} else {
includePredicate = instance -> {
Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
if (include == null) {
return false;
}
return include;
};
}
//通過注冊中心查找服務組裝路由定義資訊
return Flux.fromIterable(discoveryClient.getServices())
.map(discoveryClient::getInstances)
.filter(instances -> !instances.isEmpty())
.map(instances -> instances.get(0))
// 根據 includeExpression 表達式,過濾不符合的 ServiceInstance
.filter(includePredicate)
.map(instance -> {
String serviceId = instance.getServiceId();
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(this.routeIdPrefix + serviceId);
String uri = urlExpr.getValue(evalCtxt, instance, String.class);
routeDefinition.setUri(URI.create(uri));
final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);
//添加配置的斷言表達式
for (PredicateDefinition original : this.properties.getPredicates()) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(original.getName());
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
predicate.addArg(entry.getKey(), value);
}
routeDefinition.getPredicates().add(predicate);
}
//添加配置的過濾器
for (FilterDefinition original : this.properties.getFilters()) {
FilterDefinition filter = new FilterDefinition();
filter.setName(original.getName());
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
filter.addArg(entry.getKey(), value);
}
routeDefinition.getFilters().add(filter);
}
return routeDefinition;
});
}
String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser, ServiceInstance instance, Map.Entry<String, String> entry) {
Expression valueExpr = parser.parseExpression(entry.getValue());
return valueExpr.getValue(evalCtxt, instance, String.class);
}
private static class DelegatingServiceInstance implements ServiceInstance {
final ServiceInstance delegate;
private final DiscoveryLocatorProperties properties;
private DelegatingServiceInstance(ServiceInstance delegate, DiscoveryLocatorProperties properties) {
this.delegate = delegate;
this.properties = properties;
}
@Override
public String getServiceId() {
if (properties.isLowerCaseServiceId()) {
return delegate.getServiceId().toLowerCase();
}
return delegate.getServiceId();
}
}
從源碼可以看出,getRouteDefinitions 方法通過服務發現用戶端從注冊中心擷取服務資訊,組裝成RouteDefinition路由定義清單,并将配置中的路由斷言和過濾應用到RouteDefinition 中
六、CachingRouteDefinitionLocator 基于緩存的路由定義定位器
緩存方式的路由定義定位器,通過傳入路由定義定位器擷取路由定義并緩存到本地。通過監聽路由重新整理時間RefreshRoutesEvent 來重新整理本地緩存的路由定義資訊
public class CachingRouteDefinitionLocator implements RouteDefinitionLocator {
//路由定義定位器
private final RouteDefinitionLocator delegate;
//路由定義資訊
private final Flux<RouteDefinition> routeDefinitions;
//本地緩存集合
private final Map<String, List> cache = new HashMap();
public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
this.delegate = delegate;
this.routeDefinitions = CacheFlux.lookup(this.cache, "routeDefs", RouteDefinition.class).onCacheMissResume(() -> {
return this.delegate.getRouteDefinitions();
});
}
public Flux<RouteDefinition> getRouteDefinitions() {
return this.routeDefinitions;
}
//重新整理本地緩存,先清空本地緩存再擷取一份新的路由定義資訊存儲
public Flux<RouteDefinition> refresh() {
this.cache.clear();
return this.routeDefinitions;
}
//監聽路由重新整理事件,重新整理本地緩存的路由定義資訊
@EventListener({RefreshRoutesEvent.class})
void handleRefresh() {
this.refresh();
}
}
七、CompositeRouteDefinitionLocator 組合路由定義定位器
組合方式路由定義定位器使用組合模式進行實作,組合多個 RouteDefinitionLocator 的實作,為擷取路由定義資訊 getRouteDefinitions 提供統一入口,組合的邏輯很簡單,通過傳入的路由定義定位器作為代理,具體的路由定義實際上是由傳入的路由定義定位器産生。
public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
private final Flux<RouteDefinitionLocator> delegates;
public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) {
this.delegates = delegates;
}
public Flux<RouteDefinition> getRouteDefinitions() {
return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
}
}
源碼位址
mall-gateway 這個項目
https://gitee.com/gaibianzlp/zlp-mall-demo.git
參考連結
https://blog.csdn.net/u010647035/article/details/84448903