VDN 微服务架构搭建篇(三)基于 Nacos 的 Spring Cloud Gateway 动态路由管理
VDN 微服务架构搭建篇(三):基于 Nacos 的 Spring Cloud Gateway 动态路由管理
在微服务架构中,网关 是整个系统的入口,负责 流量管理、请求路由、安全控制等关键功能。
Spring Cloud Gateway 作为 Spring 生态官方推荐的网关方案,具备 异步非阻塞 的高性能特性,并支持 动态路由、限流、负载均衡等功能。
但在传统网关架构中,路由规则往往写死在配置文件中,每次修改都需要 手动调整配置 & 重启服务,这对于 高可用系统 来说并不友好。
本文将介绍如何使用 Spring Cloud Gateway + Nacos 配置中心 实现 动态路由管理,支持 自动发现、实时更新,让网关在运行时自动感知微服务变化,无需重启即可刷新路由规则,从而 提升微服务治理的灵活性和高可用性。
一、Spring Cloud Gateway 基础配置
1.1 添加依赖
在 pom.xml
中引入 Spring Cloud Gateway 和 Nacos 相关依赖:
<dependencies>
<!-- Spring Cloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Nacos 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Knife4j 网关文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
</dependency>
</dependencies>
1.2 在 Nacos 注册微服务
在 application.yml
配置 Nacos 相关信息,使服务可以自动注册到 Nacos:
spring:
application:
name: vdn-system
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
group: vdn
namespace: dev17
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
username: ${spring.cloud.nacos.discovery.username}
password: ${spring.cloud.nacos.discovery.password}
namespace: ${spring.cloud.nacos.discovery.namespace}
group: ${spring.cloud.nacos.discovery.group}
file-extension: yaml
1.3 配置网关的静态路由
在 application.yml
中配置 静态路由(仅用于演示,后续会改为动态路由):
spring:
cloud:
gateway:
routes:
- id: vdn-system
uri: lb://vdn-system
predicates:
- Path=/sys/**
二、Nacos 动态路由管理
2.1 在 Nacos 配置中心创建动态路由
(1)在 Nacos 控制台添加配置
- Data ID:
gateway-routes.json
- Group:
vdn
- Namespace:
dev17
- 内容(JSON 格式):
[
{
"id": "vdn-system",
"uri": "lb://vdn-system",
"predicates": [
"Path=/sys/**"
]
}
]
(2)在 bootstrap.yml
中添加以下配置
spring:
application:
name: vdn-gateway
cloud:
nacos:
discovery:
# Nacos服务器地址
server-addr: localhost:8848
username: nacos
password: nacos
group: vdn
# Nacos命名空间
namespace: dev17
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
username: ${spring.cloud.nacos.discovery.username}
password: ${spring.cloud.nacos.discovery.password}
namespace: ${spring.cloud.nacos.discovery.namespace}
# 配置分组名称
group: ${spring.cloud.nacos.discovery.group}
# 文件扩展名,指示配置文件格式
file-extension: yaml
2.2 监听 Nacos 变更 & 动态更新路由
在 NacosRouteDefinitionRepository
类中实现 监听 Nacos 变更,动态更新 Gateway 路由:
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.utils.StringUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Nacos 动态路由管理
* <p>
* 该类用于管理 Spring Cloud Gateway 的路由,并将其存储在 Nacos 配置中心中。
* 通过监听 Nacos 配置的变更,实现网关路由的动态刷新。
*/
@Component
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository {
private final Logger log = LoggerFactory.getLogger(NacosRouteDefinitionRepository.class);
// 用于发布 Spring 事件(刷新路由)
private final ApplicationEventPublisher publisher;
// Nacos 配置属性
private final NacosConfigProperties nacosConfigProperties;
// Nacos 配置管理器
private final NacosConfigManager nacosConfigManager;
// JSON 解析工具
private final ObjectMapper objectMapper;
// Nacos 中存储路由的 `dataId`
private static final String DATA_ID = "gateway-routes.json";
// 配置获取超时时间(单位:毫秒)
private static final int CONFIG_TIMEOUT_MS = 3000;
/**
* 构造方法
*
* @param publisher Spring 事件发布器,用于动态刷新网关路由
* @param nacosConfigProperties Nacos 配置属性
*/
@Autowired
public NacosRouteDefinitionRepository(ApplicationEventPublisher publisher, NacosConfigProperties nacosConfigProperties) {
this.publisher = publisher;
this.nacosConfigProperties = nacosConfigProperties;
this.nacosConfigManager = new NacosConfigManager(nacosConfigProperties);
this.objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 初始化 Nacos 配置监听器
nacosListener();
}
/**
* Nacos 配置监听器
* <p>
* 监听 Nacos 中 `gateway-routes.json` 发生变更时,触发 Gateway 重新加载路由。
*/
private void nacosListener() {
// 使用单线程线程池,避免 Listener 需要 Executor 时出错
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
nacosConfigManager.getConfigService().addListener(DATA_ID, nacosConfigProperties.getGroup(), new Listener() {
@Override
public Executor getExecutor() {
return executorService;
}
@Override
public void receiveConfigInfo(String configInfo) {
log.info("收到新的路由配置: {}", configInfo);
// 发布 Spring 事件,触发网关路由刷新
publisher.publishEvent(new RefreshRoutesEvent(this));
}
});
} catch (NacosException e) {
log.error("Nacos 监听器初始化失败", e);
} finally {
// 关闭线程池,避免资源泄露
executorService.shutdown();
}
}
/**
* 获取 Nacos 中的路由配置
* <p>
* 该方法会从 Nacos 读取 `gateway-routes.json` 配置,并解析为 RouteDefinition 列表。
*
* @return 返回 Flux<RouteDefinition>,用于 Gateway 加载路由
*/
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
// 从 Nacos 读取配置
String routeConfig = nacosConfigManager.getConfigService()
.getConfig(DATA_ID, nacosConfigProperties.getGroup(), CONFIG_TIMEOUT_MS);
// 路由列表
List<RouteDefinition> routeDefinitionList = new ArrayList<>();
// 如果配置不为空,则解析 JSON
if (StringUtils.hasText(routeConfig)) {
routeDefinitionList = objectMapper.readValue(routeConfig, new TypeReference<>() {
});
}
return Flux.fromIterable(routeDefinitionList);
} catch (Exception e) {
log.error("从 Nacos 获取路由定义失败", e);
return Flux.error(e);
}
}
/**
* 保存路由定义
* <p>
* 该方法会将新的路由定义追加到 `gateway-routes.json` 中,并同步更新到 Nacos。
*
* @param route 需要保存的路由定义
* @return 返回 Mono<Void>
*/
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(r -> {
try {
// 将新的 RouteDefinition 转换为 JSON
String routeJson = objectMapper.writeValueAsString(r);
// 发布到 Nacos
nacosConfigManager.getConfigService().publishConfig(DATA_ID, nacosConfigProperties.getGroup(), routeJson);
return Mono.empty();
} catch (Exception e) {
log.error("保存路由定义失败", e);
return Mono.error(e);
}
});
}
/**·
* 删除路由定义
* <p>
* 该方法会从 `gateway-routes.json` 中移除指定的路由 ID,并同步更新 Nacos。
*
* @param routeId 需要删除的路由 ID
* @return 返回 Mono<Void>
*/
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
try {
// 获取 Nacos 中的路由配置
String routeConfig = nacosConfigManager.getConfigService()
.getConfig(DATA_ID, nacosConfigProperties.getGroup(), CONFIG_TIMEOUT_MS);
// 解析 JSON 为 RouteDefinition 列表
List<RouteDefinition> routeDefinitionList = objectMapper.readValue(routeConfig, new TypeReference<>() {
});
// 删除指定 ID 的路由
routeDefinitionList.removeIf(rd -> rd.getId().equals(id));
// 重新生成 JSON 并更新到 Nacos
String updatedRouteJson = objectMapper.writeValueAsString(routeDefinitionList);
nacosConfigManager.getConfigService().publishConfig(DATA_ID, nacosConfigProperties.getGroup(), updatedRouteJson);
return Mono.empty();
} catch (Exception e) {
log.error("删除路由定义失败,ID: {}", id, e);
return Mono.error(e);
}
});
}
}
三、总结
- 动态路由刷新:网关无需重启,即可自动刷新路由配置。
- 中心化管理:所有路由规则存储于 Nacos 配置中心,便于维护。
- 自动发现 & 负载均衡:结合 Nacos 注册中心,可自动发现新微服务并添加路由。
通过本文的介绍,你已经掌握了 如何基于 Spring Cloud Gateway + Nacos 实现动态路由管理 🎯。
你可以在项目中直接应用这个方案,让网关更智能、更高效! 🚀🚀🚀
写在最后
上一篇:👉 VDN 微服务架构搭建篇(二)服务注册与配置中心Nacos
下一篇:👉 待完善
源码🚀🚀🚀