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-systemcloud:nacos:discovery:server-addr: 127.0.0.1:8848username: nacospassword: nacosgroup: vdnnamespace: dev17config: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-systemuri: lb://vdn-systempredicates:- 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-gatewaycloud:nacos:discovery:# Nacos服务器地址server-addr: localhost:8848username: nacospassword: nacosgroup: vdn# Nacos命名空间namespace: dev17config: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 路由:
java">
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 配置属性*/@Autowiredpublic 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() {@Overridepublic Executor getExecutor() {return executorService;}@Overridepublic 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 加载路由*/@Overridepublic Flux<RouteDefinition> getRouteDefinitions() {try {// 从 Nacos 读取配置String routeConfig = nacosConfigManager.getConfigService().getConfig(DATA_ID, nacosConfigProperties.getGroup(), CONFIG_TIMEOUT_MS);// 路由列表List<RouteDefinition> routeDefinitionList = new ArrayList<>();// 如果配置不为空,则解析 JSONif (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>*/@Overridepublic Mono<Void> save(Mono<RouteDefinition> route) {return route.flatMap(r -> {try {// 将新的 RouteDefinition 转换为 JSONString routeJson = objectMapper.writeValueAsString(r);// 发布到 NacosnacosConfigManager.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>*/@Overridepublic 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 并更新到 NacosString 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);}});}
}
三、总结
通过本文的介绍,你已经掌握了 如何基于 Spring Cloud Gateway + Nacos 实现动态路由管理 🎯。
你可以在项目中直接应用这个方案,让网关更智能、更高效! 🚀🚀🚀
写在最后
上一篇:👉 VDN 微服务架构搭建篇(二)服务注册与配置中心Nacos
下一篇:👉 待完善
源码🚀🚀🚀