Spring Gateway動(dòng)態(tài)路由實(shí)現(xiàn)方案
前沿
Spring Cloud Gateway (下文簡(jiǎn)稱 Gateway)作為微服務(wù)網(wǎng)關(guān),動(dòng)態(tài)配置路由是剛需,畢竟沒人想路由一改就要重啟網(wǎng)關(guān)。
本文結(jié)合源碼,提供一種動(dòng)態(tài)路由配置的思路。
何為路由
在 Gateway 中,路由對(duì)應(yīng)的類是org.springframework.cloud.gateway.route.Route
,各屬性含義:
- id:路由唯一標(biāo)識(shí)
- uri:轉(zhuǎn)發(fā)的目標(biāo)地址
- order:路由順序
- predicate:謂詞集合,符合條件的請(qǐng)求才會(huì)被路由
- gatewayFilters:過濾器集合,允許對(duì)請(qǐng)求轉(zhuǎn)發(fā)前后對(duì)請(qǐng)求和響應(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(); }
子類有三個(gè):
CachingRouteLocator
通過 ConcurrentHashMap 來緩存路由對(duì)象,避免路由對(duì)象反復(fù)構(gòu)建,通過監(jiān)聽 RefreshRoutesEvent 事件來刷新緩存,主要用于提升性能。
CompositeRouteLocator
RouteLocator 的裝飾器,將一組 RouteLocator 合并成一個(gè)。
RouteDefinitionRouteLocator
路由的實(shí)際構(gòu)建者,根據(jù)路由定義對(duì)象 RouteDefinition 來構(gòu)建路由,內(nèi)部會(huì)依賴 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),它會(huì)從配置文件中讀取spring.cloud.gateway.routes
來構(gòu)建 RouteDefinition,當(dāng)然也支持從配置中心讀取。
InMemoryRouteDefinitionRepository
基于內(nèi)存的實(shí)現(xiàn),內(nèi)部持有一個(gè) Map 對(duì)象來管理 RouteDefinition,默認(rèn)是空的。額外實(shí)現(xiàn)了 RouteDefinitionWriter 接口,允許新增和刪除 RouteDefinition。
DiscoveryClientRouteDefinitionLocator
基于服務(wù)發(fā)現(xiàn)的實(shí)現(xiàn),支持從服務(wù)注冊(cè)中心發(fā)現(xiàn)服務(wù),并將服務(wù)構(gòu)建成 RouteDefinition。
CompositeRouteDefinitionLocator
裝飾器實(shí)現(xiàn),用于將多個(gè) RouteDefinitionLocator 合并成一個(gè)。
RouteLocator工作流程
路由的構(gòu)建依賴兩個(gè)核心組件:RouteLocator 和 RouteDefinitionLocator。
簡(jiǎn)單來說,RouteDefinitionLocator 負(fù)責(zé)提供 RouteDefinition 路由定義對(duì)象,RouteLocator 再根據(jù) RouteDefinition 構(gòu)建路由對(duì)象。
項(xiàng)目引入spring-cloud-starter-gateway
依賴后,Spring 啟動(dòng)時(shí)會(huì)觸發(fā) Gateway 的自動(dòng)裝配,
即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 有兩個(gè),分別是: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)會(huì)自動(dòng)裝配 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)動(dòng)態(tài)路由,CachingRouteLocator 是關(guān)鍵。
默認(rèn)情況下,Gateway 會(huì)通過 CachingRouteLocator 來優(yōu)化性能。因?yàn)楦鶕?jù) RouteDefinition 來構(gòu)建路由還是有不小開銷的,CachingRouteLocator 會(huì)把構(gòu)建好的 List 緩存下來。
同時(shí),它實(shí)現(xiàn)了 ApplicationListener,通過監(jiān)聽 RefreshRoutesEvent 事件來刷新路由。
動(dòng)態(tài)路由實(shí)現(xiàn)
綜上所述,實(shí)現(xiàn)動(dòng)態(tài)路由的思路就是:
實(shí)現(xiàn)一個(gè)自定義的 RouteDefinitionLocator 從遠(yuǎn)程加載配置并構(gòu)建 RouteDefinition,路由一旦有變更,就發(fā)送一個(gè) RefreshRoutesEvent 事件通知 CachingRouteLocator 刷新路由,即可生效。
路由配置推薦 Nacos,因?yàn)樗凶兏ㄖ?。通過監(jiān)聽配置,即可在配置變更時(shí),第一時(shí)間刷新路由,讓新的路由生效。
首先,引入依賴
<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
啟動(dòng)時(shí)由 Nacos 負(fù)責(zé)初始化配置,routesListener()
方法在配置變更時(shí)觸發(fā),關(guān)鍵是變更后發(fā)送 RefreshRoutesEvent 事件,否則緩存不會(huì)刷新。
@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()會(huì)重新觸發(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)啟動(dòng),訪問127.0.0.1:8080/tb
的請(qǐng)求會(huì)被轉(zhuǎn)發(fā)到www.taobao.com
。
修改路由配置如下,網(wǎng)關(guān)無需重啟,訪問127.0.0.1:8080/jd
的請(qǐng)求會(huì)被轉(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" } } ] } ]
尾巴
Spring Cloud Gateway 動(dòng)態(tài)路由實(shí)現(xiàn)的核心思路,是通過自定義 RouteDefinitionLocator 從 Nacos 等配置中心動(dòng)態(tài)加載路由定義,并在配置變更時(shí)發(fā)布 RefreshRoutesEvent 事件觸發(fā) CachingRouteLocator 刷新路由緩存。
到此這篇關(guān)于Spring Gateway動(dòng)態(tài)路由實(shí)現(xiàn)方案的文章就介紹到這了,更多相關(guān)Spring Gateway動(dòng)態(tài)路由內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Spring Security中權(quán)限注解的使用
這篇文章主要為大家詳細(xì)介紹一下Spring Security中權(quán)限注解的使用方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定參考價(jià)值,需要的可以參考一下2022-05-05springboot基于Redis發(fā)布訂閱集群下WebSocket的解決方案
這篇文章主要介紹了springboot基于Redis發(fā)布訂閱集群下WebSocket的解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01關(guān)于MyBatis的foreach標(biāo)簽常用方法
這篇文章主要介紹了關(guān)于MyBatis的foreach標(biāo)簽常用方法,foreach 標(biāo)簽可以用來遍歷數(shù)組、列表和 Map 等集合參數(shù),實(shí)現(xiàn)批量操作或一些簡(jiǎn)單 SQL 操作,需要的朋友可以參考下2023-05-05關(guān)于Spring的@Autowired依賴注入常見錯(cuò)誤的總結(jié)
有時(shí)我們會(huì)使用@Autowired自動(dòng)注入,同時(shí)也存在注入到集合、數(shù)組等復(fù)雜類型的場(chǎng)景。這都是方便寫 bug 的場(chǎng)景,本篇文章帶你了解Spring @Autowired依賴注入的坑2021-09-09Java套接字(Socket)網(wǎng)絡(luò)編程入門
這篇文章主要介紹了Java套接字(Socket)網(wǎng)絡(luò)編程入門,Socket可以理解為是對(duì)TCP/IP協(xié)議的抽象,需要的朋友可以參考下2015-10-10java String、Json對(duì)象與byte數(shù)組轉(zhuǎn)換方式
這篇文章主要介紹了java String、Json對(duì)象與byte數(shù)組轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java Graphics實(shí)現(xiàn)界面顯示文字并換行
Java中Graphics類提供了一些基本的幾何圖形繪制方法,本文將利用Graphics實(shí)現(xiàn)界面顯示文字并換行效果,感興趣的小伙伴可以動(dòng)手嘗試一下2022-08-08