文章目录
- 前言
-
- [SpringCloud Gateway 基于 Nacos 实现动态路由](#SpringCloud Gateway 基于 Nacos 实现动态路由)
-
- [1. 作用](#1. 作用)
- [2. 使用场景](#2. 使用场景)
- [3. 测试效果](#3. 测试效果)
- [4. 关键代码如下](#4. 关键代码如下)
前言
如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。
而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!
SpringCloud Gateway 基于 Nacos 实现动态路由
1. 作用
将网关路由配置外部化托管在 Nacos 配置中心,网关监听 Nacos 配置变更,自动重新加载路由并刷新 Gateway 内置路由缓存,无需重启网关即可实现路由动态生效。
2. 使用场景
- 网关集群部署,需要统一管控路由 多台 Gateway 实例,要求修改路由所有节点同步生效。原生内存仓库只单实例生效,不满足集群需求。
- 频繁变更转发规则,不能重启网关 业务迭代、灰度发布、上下游服务扩缩容,需要随时新增 / 修改 / 下线路由,网关重启成本高,会造成请求中断。
- 路由配置统一平台管理,运维直接操作 Nacos 控制台维护路由 不需要开发独立的路由管理后台。运维 / 开发直接在 Nacos 控制台编辑路由 JSON,实现路由上下线。
- 项目 POC 验证、中小规模业务网关 想快速落地动态路由,不想实现 RouteDefinitionRepository 的 save/delete 读写逻辑,降低开发成本。
- 静态路由 + 动态路由共存 yml 保留基础固定路由(如网关自身健康检查),业务动态路由由 Nacos 提供,CompositeRouteDefinitionLocator合并两套数据源。
3. 测试效果
下面是测试的配置。

启动网关:

修改该配置的一个值。

把上述图片红框处,从8089改到8088,然后看一下服务日志的效果。




可以看到服务器日志打印信息,已经监听到nacos配置表更,刷新了配置,测试通过。
4. 关键代码如下
springboot版本: 2.6.15
springCloud版本:2021.0.9
Spring Cloud Alibaba版本:2021.0.6.0
yml nacos集成配置。
bash
spring:
application:
name: yzy-gateway-out
cloud:
nacos:
config:
file-extension: json
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
namespace: public
group: DEFAULT_GROUP
data-id: out-gateway-routes
refresh-enabled: true
shared-configs:
- data-id: out-service
group: DEFAULT_GROUP
file-extension: json
refresh-enabled: true
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
namespace: public
group: DEFAULT_GROUP
retry: # 重试配置
max-attempts: 3
delay: 1000 #重试间隔ms
timeout: 3000 # 超时配置
gateway:
discovery:
locator:
enabled: true
loadbalancer:
cache:
enabled: true
ttl: 5s
启动类:
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* yzy-gateway 网关服务启动类
* Spring Cloud Gateway + Nacos(注册中心/配置中心)
* 路由 /api/b/** → yzy-b-demo
*
* @author yzy
* @version 1.0
*/
@EnableDiscoveryClient
@SpringBootApplication
public class YzyGatewayOutApplication {
public static void main(String[] args) {
SpringApplication.run(YzyGatewayOutApplication.class, args);
}
}
网关配置:
java
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author yangzhenyu
* @version 1.0
* @date 2026/8/15 10:32
*/
@Configuration
public class GatewayConfig {
@Bean
public RouteDefinitionRepository routeDefinitionRepository(){
// 内存版路由仓库,路由全部存在 JVM 内存
return new InMemoryRouteDefinitionRepository();
}
}
Nacos动态路由服务:
java
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* Nacos动态路由服务
* <p>
* 监听Nacos配置中心路由配置变更,实时更新网关路由。
* 使用Spring Cloud Alibaba注入的NacosConfigManager获取ConfigService,
* 确保namespace认证等配置与Spring容器一致。
*
* @author yzy
* @version 1.1
*/
@Slf4j
@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
@Value("${spring.cloud.nacos.config.data-id}")
private String dataId;
@Value("${spring.cloud.nacos.config.group}")
private String group;
private final NacosConfigManager nacosConfigManager;
private final RouteDefinitionWriter routeDefinitionWriter;
private final RouteDefinitionRepository routeDefinitionRepository;
private ApplicationEventPublisher publisher;
public NacosDynamicRouteService(NacosConfigManager nacosConfigManager,
RouteDefinitionWriter routeDefinitionWriter,
RouteDefinitionRepository routeDefinitionRepository) {
this.nacosConfigManager = nacosConfigManager;
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeDefinitionRepository = routeDefinitionRepository;
}
@EventListener(ApplicationReadyEvent.class)
public void init() {
log.info("=== 初始化Nacos动态路由服务 ===");
log.info("dataId={}, group={}", dataId, group);
try {
ConfigService configService = nacosConfigManager.getConfigService();
log.info("ConfigService状态: {}", configService);
// 首次拉取配置
String configInfo = configService.getConfig(dataId, group, 5000);
log.info("首次拉取路由配置: {}", configInfo);
if (configInfo != null && !configInfo.trim().isEmpty()) {
updateRoutes(configInfo);
publisher.publishEvent(new RefreshRoutesEvent(this));
log.info("首次路由加载完成,已发布RefreshRoutesEvent");
} else {
log.warn("Nacos中未找到路由配置,dataId={}, group={}", dataId, group);
}
// 注册配置变更监听器
configService.addListener(dataId, group, new Listener() {
@Override
public Executor getExecutor() {
return Runnable::run;
}
@Override
public void receiveConfigInfo(String configInfo) {
log.info("=== 收到Nacos配置变更通知 ===");
log.info("新配置内容: {}", configInfo);
if (configInfo != null && !configInfo.trim().isEmpty()) {
updateRoutes(configInfo);
publisher.publishEvent(new RefreshRoutesEvent(this));
log.info("路由刷新完成,已发布RefreshRoutesEvent");
}
}
});
log.info("Nacos配置监听器注册成功,等待配置变更...");
} catch (NacosException e) {
log.error("Nacos配置监听初始化失败", e);
}
}
/**
* 反射清空InMemoryRouteDefinitionRepository内部全部路由
*/
@SuppressWarnings("unchecked")
private void clearAllRoutes() {
if (!(routeDefinitionRepository instanceof InMemoryRouteDefinitionRepository)) {
log.warn("当前不是内存仓库,跳过清空");
return;
}
try {
InMemoryRouteDefinitionRepository memoryRepo = (InMemoryRouteDefinitionRepository) routeDefinitionRepository;
Field field = InMemoryRouteDefinitionRepository.class.getDeclaredField("routes");
field.setAccessible(true);
Map<String, RouteDefinition> routesMap = (Map<String, RouteDefinition>) field.get(memoryRepo);
int before = routesMap.size();
routesMap.clear();
log.info("反射清空内存路由完成,清除{}条旧路由", before);
} catch (Exception e) {
log.error("反射清空路由失败", e);
}
}
/**
* 解析JSON路由配置并更新内存路由仓库
*
* @param configInfo Nacos返回的JSON格式路由配置
*/
private void updateRoutes(String configInfo) {
try {
List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
if (routeDefinitions == null) {
log.warn("路由解析结果为空");
return;
}
log.info("解析到{}条路由定义", routeDefinitions.size());
for (RouteDefinition rd : routeDefinitions) {
log.info(" 路由: id={}, uri={}, predicates={}",
rd.getId(), rd.getUri(), rd.getPredicates());
}
// 反射清空全部旧路由
clearAllRoutes();
// 写入新路由
for (RouteDefinition definition : routeDefinitions) {
routeDefinitionWriter.save(Mono.just(definition))
.subscribe(
unused -> log.info(" 保存路由成功: id={}", definition.getId()),
err -> log.error(" 保存路由id={}失败", definition.getId(), err)
);
}
log.info("路由更新完成,共{}条", routeDefinitions.size());
} catch (Exception e) {
log.error("解析/更新路由失败", e);
}
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}