源碼梳理:
RouteDefinitionLocator 是路由定義定位器的頂級接口,它的主要作用就是讀取路由的配置信息(org.springframework.cloud.gateway.route.RouteDefinition)。它有五種不同的實現類,如圖:
RouteDefinitionLocator
類 : org.springframework.cloud.gateway.route.RouteDefinitionLocator;路由定義定位器接口,只有一個方法,用來獲取路由定義列表的方法。
public interface RouteDefinitionLocator { Flux<RouteDefinition> getRouteDefinitions(); }
通過 RouteDefinitionLocator 的類圖,可以看出該接口有多個實現類:
1、PropertiesRouteDefinitionLocator:基於屬性配置
2、DiscoveryClientRouteDefinitionLocator:基於服務發現
3、CompositeRouteDefinitionLocator:組合方式
4、CachingRouteDefinitionLocator:緩存方式
5、其中還有一個接口 RouteDefinitionRepository 繼承自RouteDefinitionLocator,用於對路由定義的操作(保存、刪除路由定義)
RouteDefinitionRepository & InMemoryRouteDefinitionRepository
RouteDefinitionRepository 接口中的方法用來對 RouteDefinition 進行增、刪、查操作
- RouteDefinitionLocator 定義了用來獲取路由定義列表的方法:
public interface RouteDefinitionLocator { Flux<RouteDefinition> getRouteDefinitions(); }
- RouteDefinitionWriter 定義了 寫和刪除的方法
public interface RouteDefinitionWriter { Mono<Void> save(Mono<RouteDefinition> route); Mono<Void> delete(Mono<String> routeId); }
RouteDefinitionRepository 接口負責將兩個以上兩個接口方法匯集:
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter { }
InMemoryRouteDefinitionRepository 實現了 RouteDefinitionRepository 接口,基於內存的路由定義倉庫,將 路由信息存儲再 內存中的 Map,提供服務的:讀、寫、刪除
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository { /** * 用來存儲路由信息的 Map */ private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap()); /** * 保存路由定義到內存中 * * @param route * @return */ @Override public Mono<Void> save(Mono<RouteDefinition> route) { return route.flatMap((r) -> { this.routes.put(r.getId(), r); return Mono.empty(); }); } /** * 根據路由id從內存中刪除指定路由定義 * * @param routeId * @return */ @Override 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)); }); } }); } /** * 獲取內存中路由定義列表 * * @return */ @Override 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; } @Override public Flux<RouteDefinition> getRouteDefinitions() { return Flux.fromIterable(this.properties.getRoutes()); } }
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
RouteDefinition (定義描述 Gateway 路由屬性的類)
RouteDefinition 作為GatewayProperties中的屬性,在網關啟動的時候讀取配置文件中的相關配置信息,源碼 其屬性如下:
@Validated public class RouteDefinition { /** * 路由唯一ID ,默認為 UUID */ private String id; /** * 路由斷言定義列表 */ @NotEmpty @Valid private List<PredicateDefinition> predicates = new ArrayList(); /** * 過濾器定義列表 */ @Valid private List<FilterDefinition> filters = new ArrayList(); /** * 轉發地址 */ @NotNull private URI uri; /** * */ private Map<String, Object> metadata = new HashMap(); /** * 優先級 */ private int order = 0; public RouteDefinition() { } public RouteDefinition(String text) { int eqIdx = text.indexOf("="); if (eqIdx <= 0) { throw new ValidationException("Unable to parse RouteDefinition text '" + text + "'" + ", must be of the form name=value"); } setId(text.substring(0, eqIdx)); String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ","); setUri(URI.create(args[0])); for (int i = 1; i < args.length; i++) { this.predicates.add(new PredicateDefinition(args[i])); } } }
進入 斷言 和 路由器 屬性可以看到他們 是一個 Map 數據結構,可以存放多個對應的 鍵值對數組
實操:(自定義路信息存儲管理)
1、自定義一個路由信息描述類 存儲路由信息的對象,繼承 RouteDefinition 根據自己的需求可自行擴展增加屬性字段(使用數據庫對應字段,可自行靈活的擴展屬性字段),數據庫中存儲此 類 對象
/** * 擴展此類支持序列化路由,(可將路由信息添加到數據庫及Redis) */ @Data @EqualsAndHashCode(callSuper = false) public class RouteDefinitionInDb extends RouteDefinition implements Serializable { private static final long serialVersionUID = 1L; /** * 路由名稱 */ private String routeName; }
2、路由數據的:增、刪、讀 的類(這里初始化加載路由信息也放在同一個類中處理,實際開發中可自行拆解,這里的示例 代碼並沒有處理 路由存儲 MySQL 和 Redis ,讀者請自行實現)
/** * 自定義路由系列化加載配置,負責對路由信息的讀、寫、刪除的方法 */ @Slf4j @Component public class RouteDefinitionWriterAndReader implements RouteDefinitionRepository { /** * 這個是 Gateway 默認加載路由信息的配置(示例代碼並沒有使用Redis 和 MySQL ,讀者自行變更為 MySQL 中存儲,啟動時加載到 Redis 中即可) */ @Autowired private GatewayProperties properties; /** * 后續改為 Redis 和 數據庫方式存儲 路由信息,此處改為相應的 RedisTemplate */ private static volatile Map<String, RouteDefinitionInDb> ROUTES_MAP = new ConcurrentHashMap<>(1 << 4); /** * 初始化配置文件中的路由到自定義容器中,后續直接從數據庫中存儲到 Redis 去 */ @PostConstruct public void initRoute() { properties.getRoutes().forEach(route -> { RouteDefinitionInDb routeInDb = new RouteDefinitionInDb(); BeanUtil.copyProperties(route, routeInDb); // 暫時沒有路由名稱,ID代替 routeInDb.setRouteName("路由名稱:" + route.getId()); log.info("加載路由:{},路由信息為:{}", routeInDb.getRouteName(), JSON.toJSONString(routeInDb)); ROUTES_MAP.put(route.getId(), routeInDb); }); log.info("路由加載完畢,路由數量為:{}", ROUTES_MAP.size()); } /** * 獲取路由配置 * * @return */ @Override public Flux<RouteDefinition> getRouteDefinitions() { // RouteDefinition 后續可改為數據庫存儲的對象 RouteDefinitionInDb List<RouteDefinitionInDb> routesInDb = ROUTES_MAP.entrySet().stream() .map(routeEntry -> routeEntry.getValue()) .collect(Collectors.toList()); return Flux.fromIterable(routesInDb); } /** * 保存路由配置(暫時未實現具體邏輯) * * @param route * @return */ @Override public Mono<Void> save(Mono<RouteDefinition> route) { // 暫時使用默認方式配置,即使用配置文件讀取的方式配置,后續改為Redis存儲 Mono<Void> mono = route.flatMap(r -> { RouteDefinitionInDb routeInDb = new RouteDefinitionInDb(); BeanUtil.copyProperties(r, routeInDb); ROUTES_MAP.put(routeInDb.getId(), routeInDb); return Mono.empty(); }); return mono; } /** * 刪除路由配置(暫時未實現具體邏輯) * * @param routeId * @return */ @Override public Mono<Void> delete(Mono<String> routeId) { // 時使用默認方式配置,即使用配置文件讀取的方式配置,后續改為Redis存儲 return routeId.flatMap(id -> { if (ROUTES_MAP.containsKey(id)) { ROUTES_MAP.remove(id); return Mono.empty(); } return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId))); }); } }