使用Spring Cloud Gateway實(shí)現(xiàn)動態(tài)路由的核心原理
動態(tài)路由將路由規(guī)則存儲在外部數(shù)據(jù)源(如數(shù)據(jù)庫、Nacos、Apollo 等),并通過事件監(jiān)聽或配置中心推送機(jī)制實(shí)時更新路由
一、動態(tài)路由的核心原理
Spring Cloud Gateway 的路由信息由 RouteDefinitionLocator 接口提供,默認(rèn)從配置文件(application.yml)加載。要實(shí)現(xiàn)動態(tài)路由,需自定義 RouteDefinitionLocator 或通過事件刷新路由緩存:
- 路由規(guī)則存儲在外部數(shù)據(jù)源(如 Nacos);
- 網(wǎng)關(guān)啟動時從數(shù)據(jù)源加載初始路由;
- 當(dāng)數(shù)據(jù)源中的路由規(guī)則變更時,通過監(jiān)聽機(jī)制(如 Nacos 配置變更通知)觸發(fā)路由刷新;
- 調(diào)用 Gateway 提供的
RouteDefinitionWriter接口更新內(nèi)存中的路由,并發(fā)布RefreshRoutesEvent事件通知網(wǎng)關(guān)重新加載路由。
二、基于 Nacos 實(shí)現(xiàn)動態(tài)路由(推薦)
Nacos 作為配置中心,支持配置變更實(shí)時推送,是實(shí)現(xiàn)動態(tài)路由的理想選擇。以下是詳細(xì)步驟:
1. 環(huán)境準(zhǔn)備
引入依賴(Maven):
<!-- Spring Cloud Gateway 核心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos 配置中心(用于存儲路由規(guī)則) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Nacos 服務(wù)發(fā)現(xiàn)(可選,用于服務(wù)名路由) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>配置 Nacos 地址(bootstrap.yml,需在 application.yml 之前加載):
spring:
application:
name: gateway-service # 服務(wù)名,對應(yīng) Nacos 配置的 Data ID
cloud:
nacos:
config:
server-addr: localhost:8848 # Nacos 服務(wù)器地址
file-extension: yaml # 配置文件格式2. 在 Nacos 中定義路由規(guī)則
登錄 Nacos 控制臺(http://localhost:8848),創(chuàng)建配置:
- Data ID:
gateway-service.yaml(與spring.application.name一致) - Group:
DEFAULT_GROUP(默認(rèn)) - 配置內(nèi)容(標(biāo)準(zhǔn)的 Gateway 路由規(guī)則):
spring:
cloud:
gateway:
routes:
- id: user-service-route
uri: lb://user-service # 負(fù)載均衡到 user-service 服務(wù)
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1 # 去除路徑前綴 /api
- id: order-service-route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=13. 配置動態(tài)路由刷新機(jī)制
Nacos 配置變更時,會自動推送新配置到網(wǎng)關(guān),需通過 @RefreshScope 使路由配置生效。但默認(rèn)情況下,Gateway 不會自動刷新路由緩存,需自定義配置類監(jiān)聽配置變更并刷新路由:
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosPropertySourceRepository;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.Executor;
@Configuration
public class NacosDynamicRouteConfig implements ApplicationEventPublisherAware {
@Autowired
private NacosConfigManager nacosConfigManager;
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher publisher;
// 路由配置的 Data ID(與 Nacos 中一致)
private static final String DATA_ID = "gateway-service.yaml";
// 路由配置的 Group
private static final String GROUP = "DEFAULT_GROUP";
@PostConstruct
public void init() throws NacosException {
// 1. 初始化加載路由配置
String configInfo = nacosConfigManager.getConfigService().getConfig(DATA_ID, GROUP, 5000);
loadRouteConfig(configInfo);
// 2. 監(jiān)聽 Nacos 配置變更
nacosConfigManager.getConfigService().addListener(DATA_ID, GROUP, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
// 配置變更時重新加載路由
loadRouteConfig(configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
});
}
// 解析配置并更新路由
private void loadRouteConfig(String configInfo) {
try {
// 從配置中解析出 RouteDefinition 列表(需自定義解析邏輯,或借助 Spring 配置綁定)
List<RouteDefinition> routeDefinitions = parseRouteDefinitions(configInfo);
// 先清空舊路由
routeDefinitionWriter.delete(Mono.just("*")).block();
// 再添加新路由
if (!CollectionUtils.isEmpty(routeDefinitions)) {
routeDefinitions.forEach(route -> {
routeDefinitionWriter.save(Mono.just(route)).block();
});
}
// 發(fā)布刷新事件,通知網(wǎng)關(guān)更新路由
this.publisher.publishEvent(new RefreshRoutesEvent(this));
} catch (Exception e) {
e.printStackTrace();
}
}
// 解析配置字符串為 RouteDefinition 列表(需根據(jù)實(shí)際配置格式實(shí)現(xiàn))
private List<RouteDefinition> parseRouteDefinitions(String configInfo) {
// 示例:使用 Spring 的 YamlPropertiesFactoryBean 解析配置
// 實(shí)際需根據(jù) configInfo 中的內(nèi)容提取 spring.cloud.gateway.routes 節(jié)點(diǎn)
// 此處省略具體解析邏輯,可參考 Spring Cloud Gateway 的配置綁定方式
return null;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}關(guān)鍵邏輯:
- 初始化時從 Nacos 加載路由配置并添加到網(wǎng)關(guān);
- 監(jiān)聽 Nacos 配置變更,當(dāng)路由規(guī)則更新時,先刪除舊路由,再添加新路由,最后發(fā)布
RefreshRoutesEvent事件觸發(fā)網(wǎng)關(guān)刷新。
4. 測試動態(tài)路由
- 啟動網(wǎng)關(guān)服務(wù),驗(yàn)證初始路由是否生效(如訪問
/api/user/1能否轉(zhuǎn)發(fā)到user-service); - 在 Nacos 控制臺修改路由規(guī)則(如新增一個路由或修改路徑),無需重啟網(wǎng)關(guān);
- 再次訪問新路由,驗(yàn)證是否生效(如新增
/api/product/**路由后,訪問該路徑能否轉(zhuǎn)發(fā)到product-service)。
三、基于數(shù)據(jù)庫實(shí)現(xiàn)動態(tài)路由(自定義數(shù)據(jù)源)
若需更靈活的路由管理(如通過后臺系統(tǒng)增刪改路由),可將路由規(guī)則存儲在數(shù)據(jù)庫(如 MySQL),通過定時任務(wù)或事件監(jiān)聽刷新路由。
1. 數(shù)據(jù)庫設(shè)計
創(chuàng)建路由規(guī)則表(gateway_route):
CREATE TABLE `gateway_route` (
`id` varchar(64) NOT NULL COMMENT '路由ID',
`uri` varchar(255) NOT NULL COMMENT '目標(biāo)地址(如 lb://user-service)',
`predicates` text COMMENT '斷言規(guī)則(JSON格式,如 [{"name":"Path","args":{"pattern":"/api/user/**"}}])',
`filters` text COMMENT '過濾器規(guī)則(JSON格式)',
`order` int(11) DEFAULT 0 COMMENT '路由優(yōu)先級(值越小越優(yōu)先)',
`status` tinyint(1) DEFAULT 1 COMMENT '狀態(tài)(1-啟用,0-禁用)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;2. 自定義 RouteDefinitionLocator
實(shí)現(xiàn) RouteDefinitionLocator 接口,從數(shù)據(jù)庫加載路由:
@Configuration
public class DbRouteDefinitionLocator implements RouteDefinitionLocator {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
// 從數(shù)據(jù)庫查詢啟用的路由
List<RouteDefinition> routes = jdbcTemplate.query(
"SELECT id, uri, predicates, filters, `order` FROM gateway_route WHERE status = 1",
(rs, rowNum) -> {
RouteDefinition route = new RouteDefinition();
route.setId(rs.getString("id"));
route.setUri(URI.create(rs.getString("uri")));
route.setOrder(rs.getInt("order"));
// 解析斷言(JSON -> List<PredicateDefinition>)
String predicatesJson = rs.getString("predicates");
List<PredicateDefinition> predicates = JSON.parseArray(predicatesJson, PredicateDefinition.class);
route.setPredicates(predicates);
// 解析過濾器(JSON -> List<FilterDefinition>)
String filtersJson = rs.getString("filters");
List<FilterDefinition> filters = JSON.parseArray(filtersJson, FilterDefinition.class);
route.setFilters(filters);
return route;
}
);
return Flux.fromIterable(routes);
}
}3. 定時刷新路由
通過定時任務(wù)定期從數(shù)據(jù)庫加載最新路由并刷新:
@Component
public class DbRouteRefreshTask {
@Autowired
private DbRouteDefinitionLocator dbRouteLocator;
@Autowired
private RouteDefinitionWriter routeWriter;
@Autowired
private ApplicationEventPublisher publisher;
// 每30秒刷新一次
@Scheduled(fixedRate = 30000)
public void refreshRoutes() {
try {
// 清空舊路由
routeWriter.delete(Mono.just("*")).block();
// 加載新路由
dbRouteLocator.getRouteDefinitions().collectList().block()
.forEach(route -> routeWriter.save(Mono.just(route)).block());
// 發(fā)布刷新事件
publisher.publishEvent(new RefreshRoutesEvent(this));
} catch (Exception e) {
e.printStackTrace();
}
}
}四、關(guān)鍵注意事項(xiàng)
- 路由ID唯一性:確保路由
id唯一,避免更新時沖突; - 配置格式正確性:動態(tài)路由的斷言(
predicates)和過濾器(filters)格式需與 Gateway 要求一致,否則會加載失??; - 性能考量:頻繁刷新路由可能影響網(wǎng)關(guān)性能,建議合理設(shè)置刷新間隔(如 Nacos 推送機(jī)制可實(shí)時更新,無需定時任務(wù));
- 容錯處理:解析路由配置時需添加異常處理,避免單個路由配置錯誤導(dǎo)致整體路由失效。
總結(jié)
Spring Cloud Gateway 實(shí)現(xiàn)動態(tài)路由的核心是將路由規(guī)則從靜態(tài)配置遷移到外部數(shù)據(jù)源,并通過事件機(jī)制實(shí)時刷新路由緩存。基于 Nacos 等配置中心的方案適合需要頻繁調(diào)整路由的場景,而基于數(shù)據(jù)庫的方案適合需要通過業(yè)務(wù)系統(tǒng)管理路由的場景。兩種方式均可實(shí)現(xiàn)無需重啟網(wǎng)關(guān)即可更新路由,提升微服務(wù)架構(gòu)的靈活性和可維護(hù)性。
到此這篇關(guān)于如何使用Spring Cloud Gateway實(shí)現(xiàn)動態(tài)路由?的文章就介紹到這了,更多相關(guān)Spring Cloud Gateway動態(tài)路由內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringCloud GateWay動態(tài)路由用法
- springcloud?gateway高級功能之集成apollo后動態(tài)刷新路由方式
- SpringCloud Gateway動態(tài)路由配置詳解
- Nacos+Spring Cloud Gateway動態(tài)路由配置實(shí)現(xiàn)步驟
- SpringCloud Gateway 利用 Mysql 實(shí)現(xiàn)動態(tài)路由的方法
- spring-cloud-gateway動態(tài)路由的實(shí)現(xiàn)方法
- SpringCloud Gateway使用redis實(shí)現(xiàn)動態(tài)路由的方法
- Spring Cloud Gateway + Nacos 實(shí)現(xiàn)動態(tài)路由
- 基于Nacos實(shí)現(xiàn)Spring Cloud Gateway實(shí)現(xiàn)動態(tài)路由的方法
相關(guān)文章
Java多線程編程實(shí)戰(zhàn)之模擬大量數(shù)據(jù)同步
這篇文章主要介紹了Java多線程編程實(shí)戰(zhàn)之模擬大量數(shù)據(jù)同步,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
Java Web項(xiàng)目中編寫定時任務(wù)的實(shí)現(xiàn)
本篇文章主要介紹了Java Web項(xiàng)目中編寫定時任務(wù)的實(shí)現(xiàn),具有一定的參考價值,有興趣的可以了解一下。2017-01-01
Java基于接口實(shí)現(xiàn)模擬動物聲音代碼實(shí)例
這篇文章主要介紹了Java基于接口實(shí)現(xiàn)模擬動物聲音代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
創(chuàng)建springboot+maven項(xiàng)目全過程
這篇文章主要介紹了創(chuàng)建springboot+maven項(xiàng)目全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-06-06
Mybatis-plus配置多數(shù)據(jù)源,連接多數(shù)據(jù)庫方式
這篇文章主要介紹了Mybatis-plus配置多數(shù)據(jù)源,連接多數(shù)據(jù)庫方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06
淺談idea中導(dǎo)入maven項(xiàng)目的兩種方式
本文主要介紹了淺談idea中導(dǎo)入maven項(xiàng)目的兩種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08

