Spring Gateway動態(tài)路由的實(shí)現(xiàn)方案
前沿
Spring Cloud Gateway (下文簡稱 Gateway)作為微服務(wù)網(wǎng)關(guān),動態(tài)配置路由是剛需,畢竟沒人想路由一改就要重啟網(wǎng)關(guān)。
本文結(jié)合源碼,提供一種動態(tài)路由配置的思路。
何為路由
在 Gateway 中,路由對應(yīng)的類是org.springframework.cloud.gateway.route.Route,各屬性含義:
- id:路由唯一標(biāo)識
- uri:轉(zhuǎn)發(fā)的目標(biāo)地址
- order:路由順序
- predicate:謂詞集合,符合條件的請求才會被路由
- gatewayFilters:過濾器集合,允許對請求轉(zhuǎn)發(fā)前后對請求和響應(yīng)做修改
- metadata:路由元數(shù)據(jù)
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;
}
路由是怎么來的?
Gateway 提供了org.springframework.cloud.gateway.route.RouteLocator接口用來構(gòu)建路由。
public interface RouteLocator {
Flux<Route> getRoutes();
}

子類有三個:
CachingRouteLocator
通過 ConcurrentHashMap 來緩存路由對象,避免路由對象反復(fù)構(gòu)建,通過監(jiān)聽 RefreshRoutesEvent 事件來刷新緩存,主要用于提升性能。
CompositeRouteLocator
RouteLocator 的裝飾器,將一組 RouteLocator 合并成一個。
RouteDefinitionRouteLocator
路由的實(shí)際構(gòu)建者,根據(jù)路由定義對象 RouteDefinition 來構(gòu)建路由,內(nèi)部會依賴 RoutePredicateFactory,GatewayFilterFactory 工廠類。
RouteDefinition
為了構(gòu)建路由,Gateway 提供了org.springframework.cloud.gateway.route.RouteDefinition路由定義類,根據(jù) RouteDefinition 來構(gòu)建 Route。
RouteDefinition 是怎么來的?
和構(gòu)建路由一樣,Gateway 也提供了 RouteDefinitionLocator 接口來發(fā)現(xiàn) RouteDefinition。
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}

常見的子類有:
PropertiesRouteDefinitionLocator
基于配置文件的實(shí)現(xiàn),它會從配置文件中讀取spring.cloud.gateway.routes來構(gòu)建 RouteDefinition,當(dāng)然也支持從配置中心讀取。
InMemoryRouteDefinitionRepository
基于內(nèi)存的實(shí)現(xiàn),內(nèi)部持有一個 Map 對象來管理 RouteDefinition,默認(rèn)是空的。額外實(shí)現(xiàn)了 RouteDefinitionWriter 接口,允許新增和刪除 RouteDefinition。
DiscoveryClientRouteDefinitionLocator
基于服務(wù)發(fā)現(xiàn)的實(shí)現(xiàn),支持從服務(wù)注冊中心發(fā)現(xiàn)服務(wù),并將服務(wù)構(gòu)建成 RouteDefinition。
CompositeRouteDefinitionLocator
裝飾器實(shí)現(xiàn),用于將多個 RouteDefinitionLocator 合并成一個。
RouteLocator工作流程
路由的構(gòu)建依賴兩個核心組件:RouteLocator 和 RouteDefinitionLocator。
簡單來說,RouteDefinitionLocator 負(fù)責(zé)提供 RouteDefinition 路由定義對象,RouteLocator 再根據(jù) RouteDefinition 構(gòu)建路由對象。

項目引入spring-cloud-starter-gateway依賴后,Spring 啟動時會觸發(fā) Gateway 的自動裝配,
即org.springframework.cloud.gateway.config.GatewayAutoConfiguration類。
Gateway 路由定義發(fā)現(xiàn)依賴一組 RouteDefinitionLocator,再把他們包裝成 CompositeRouteDefinitionLocator
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
默認(rèn)的 RouteDefinitionLocator 有兩個,分別是:PropertiesRouteDefinitionLocator 和 InMemoryRouteDefinitionRepository。前者讀取配置文件來生成 RouteDefinition,后者使用 Map 來維護(hù),默認(rèn)是空的。
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
RouteDefinitionLocator 有了,接下來就是 RouteLocator。默認(rèn)會自動裝配 RouteDefinitionRouteLocator 和 CachingRouteLocator,前者根據(jù) RouteDefinition 來構(gòu)建路由,后者提供緩存優(yōu)化性能。
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties,
configurationService);
}
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
實(shí)現(xiàn)動態(tài)路由,CachingRouteLocator 是關(guān)鍵。
默認(rèn)情況下,Gateway 會通過 CachingRouteLocator 來優(yōu)化性能。因?yàn)楦鶕?jù) RouteDefinition 來構(gòu)建路由還是有不小開銷的,CachingRouteLocator 會把構(gòu)建好的 List 緩存下來。
同時,它實(shí)現(xiàn)了 ApplicationListener,通過監(jiān)聽 RefreshRoutesEvent 事件來刷新路由。

動態(tài)路由實(shí)現(xiàn)
綜上所述,實(shí)現(xiàn)動態(tài)路由的思路就是:
實(shí)現(xiàn)一個自定義的 RouteDefinitionLocator 從遠(yuǎn)程加載配置并構(gòu)建 RouteDefinition,路由一旦有變更,就發(fā)送一個 RefreshRoutesEvent 事件通知 CachingRouteLocator 刷新路由,即可生效。
路由配置推薦 Nacos,因?yàn)樗凶兏ㄖ?。通過監(jiān)聽配置,即可在配置變更時,第一時間刷新路由,讓新的路由生效。
首先,引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
然后,實(shí)現(xiàn)自定義的 RouteDefinitionLocator,目的是從 Nacos 讀取路由配置,構(gòu)建 RouteDefinition。這里以 JSON 格式配置,routesJson啟動時由 Nacos 負(fù)責(zé)初始化配置,routesListener()方法在配置變更時觸發(fā),關(guān)鍵是變更后發(fā)送 RefreshRoutesEvent 事件,否則緩存不會刷新。
@Component
public class DynamicRouteDefinitionLocator implements RouteDefinitionLocator {
@NacosConfig(dataId = "gateway-routes.json", group = "DEFAULT_GROUP")
private String routesJson;
private final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@NacosConfigListener(dataId = "gateway-routes.json", group = "DEFAULT_GROUP")
public void routesListener(String data) {
this.routesJson = data;
// 發(fā)送路由刷新事件,getRouteDefinitions()會重新觸發(fā)
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
return Flux.fromIterable(OBJECT_MAPPER.readerForListOf(RouteDefinition.class).readValue(routesJson));
} catch (JsonProcessingException e) {
return Flux.empty();
}
}
}
在 Nacos 中配置,Data ID=gateway-routes.json,Group=DEFAULT_GROUP,內(nèi)容如下:
[
{
"id": "tb_route",
"uri": "https://www.taobao.com",
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/tb/**"
}
}
],
"filters": [
{
"name": "StripPrefix",
"args": {
"parts": "1"
}
}
]
}
]
網(wǎng)關(guān)啟動,訪問127.0.0.1:8080/tb的請求會被轉(zhuǎn)發(fā)到www.taobao.com。
修改路由配置如下,網(wǎng)關(guān)無需重啟,訪問127.0.0.1:8080/jd的請求會被轉(zhuǎn)發(fā)到www.jd.com。
[
{
"id": "jd_route",
"uri": "https://www.jd.com/",
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/jd/**"
}
}
],
"filters": [
{
"name": "StripPrefix",
"args": {
"parts": "1"
}
}
]
}
]
總結(jié)
Spring Cloud Gateway 動態(tài)路由實(shí)現(xiàn)的核心思路,是通過自定義 RouteDefinitionLocator 從 Nacos 等配置中心動態(tài)加載路由定義,并在配置變更時發(fā)布 RefreshRoutesEvent 事件觸發(fā) CachingRouteLocator 刷新路由緩存。
以上就是Spring Gateway動態(tài)路由的實(shí)現(xiàn)方案的詳細(xì)內(nèi)容,更多關(guān)于Spring Gateway動態(tài)路由的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java Spring MVC4環(huán)境搭建實(shí)例詳解(步驟)
spring WEB MVC框架提供了一個MVC(model-view-controller)模型-視圖-控制器的結(jié)構(gòu)和組件,利用它可以開發(fā)更靈活、松耦合的web應(yīng)用。MVC模式使得整個服務(wù)應(yīng)用的各部分(控制邏輯、業(yè)務(wù)邏輯、UI界面展示)分離開來,使它們之間的耦合性更低2017-08-08
如何修改json字符串中某個key對應(yīng)的value值
這篇文章主要介紹了如何修改json字符串中某個key對應(yīng)的value值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11
Java的NIO之并發(fā)環(huán)境下非阻塞IO技術(shù)詳解
這篇文章主要介紹了Java的NIO之并發(fā)環(huán)境下非阻塞IO技術(shù)詳解,Java NIO(New IO)是Java平臺提供的一種用于高效處理I/O操作的API,它引入了一組新的類和概念,以提供更好的性能和可擴(kuò)展性,需要的朋友可以參考下2023-09-09
SpringBoot集成Spring Data JPA及讀寫分離
這篇文章主要介紹了SpringBoot集成Spring Data JPA及讀寫分離的相關(guān)知識,需要的朋友可以參考下2017-04-04
Hikari連接池使用SpringBoot配置JMX監(jiān)控實(shí)現(xiàn)
Hikari是Spring Boot默認(rèn)的數(shù)據(jù)庫連接池。區(qū)別于C3P0直接通過連接池對象獲取各項狀態(tài)指標(biāo),Hikari需要通過JMX來獲取。本文就詳細(xì)的來介紹一下,感興趣的可以了解一下2021-07-07

