当前位置: 首页 > article >正文

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);
            }
        });
    }
}


三、总结

  1. 动态路由刷新:网关无需重启,即可自动刷新路由配置。
  2. 中心化管理:所有路由规则存储于 Nacos 配置中心,便于维护。
  3. 自动发现 & 负载均衡:结合 Nacos 注册中心,可自动发现新微服务并添加路由。

通过本文的介绍,你已经掌握了 如何基于 Spring Cloud Gateway + Nacos 实现动态路由管理 🎯。
你可以在项目中直接应用这个方案,让网关更智能、更高效! 🚀🚀🚀


写在最后

上一篇:👉 VDN 微服务架构搭建篇(二)服务注册与配置中心Nacos
下一篇:👉 待完善

源码🚀🚀🚀


http://www.kler.cn/a/536265.html

相关文章:

  • 在 Flownex 中创建自定义工作液
  • 机器学习数学基础:14.矩阵的公式
  • 【医院绩效管理专题】2.绩效管理:医院发展的核心驱动力
  • C++ 课程学习笔记:从对象生命周期看资源管理之道
  • 1-R语言概述
  • 第二次连接k8s平台注意事项
  • 如何使用iframe来渲染ThingsBoard仪表盘
  • LabVIEW与PLC交互
  • 【Spring】什么是Spring?
  • SAP物料账未分配差异-采购发票数量大于库存数量
  • 多无人机--强化学习
  • 20.责任链模式(Chain of Responsibility Pattern)
  • 搜索+图论1 练习答案+思路
  • 蓝桥算法基础2
  • EtherCAT帧捕获与帧结构分析
  • 基于Bootstrap + Java + Oracle实现的电商平台
  • DeepSeek图解10页PDF
  • STM32自学记录(八)
  • 【ArcGIS Pro 简介1】
  • Docker Desktop安装kubernetes时一直在Starting:Kubernetes failed to start
  • Day56_20250204_图论part1_图论理论基础|深搜理论基础|98.所有可达路径|广搜理论基础
  • Jetson AGX Orin折腾记
  • PEP8代码规范
  • Rust语言进阶之标准输出:stdout用法实例(一百零六)
  • vue高级面试题
  • Mac本地体验LM studio