实现springCloud GateWay结合Nacos的动态路由配置过程

 更新时间:2026年06月26日 08:51:17   作者:smtp-pop3  
本文详细介绍了如何使用SpringCloudGateWay与Nacos进行集成,包括配置管理、动态路由更新以及Nacos配置的实时同步等内容,通过实体类读取配置信息、编写网关与Nacos连接类,实现了网关服务的动态配置更新

1.创建SpringCloud GateWay 

并在yaml文件中配置好nacos

spring:
  application:
    name: e-commerce-gateway
  cloud:
    nacos:
      discovery:
        enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可
        namespace: ....................
        server-addr: .....................
        metadata:
          management:
            context-path: ${server.servlet.context-path}/actuator
# 这个地方独立配置, 是网关的数据, 代码 GatewayConfig.java 中读取被监听
nacos:
  gateway:
    route:
      config:
        data-id: e-commerce-gateway-route
        group: e-commerce

2.在nacos中为网关服务创建配置管理

3.在网关模块中编写一个实体类

以便从配置文件中读取配置信息

/**
 * 1. @ClassDescription:nacos相关信息
 * 2. @author: Dongkoer
 * 3. @date: 2023年10月19日 11:07
 */
@Configuration
public class GatewayConfig {
    //读取配置的超时时间
    public static final long DEFAULT_TIMEOUT = 30000;


    //命名空间

    public static String namespace;

    //服务器地址

    public static String serverAddr;


    //dataID

    public static String nacosRouteDataId;

    //分组ID

    public static String nacosRouteGroup;



    @Value("${spring.cloud.nacos.discovery.namespace}")
    public  void setNamespace(String namespace) {
        GatewayConfig.namespace = namespace;
    }


    @Value("${spring.cloud.nacos.discovery.server-addr}")
    public void setServerAddr(String serverAddr) {
        GatewayConfig.serverAddr = serverAddr;
    }


    @Value("${nacos.gateway.route.config.data-id}")
    public  void setNacosRouteDataId(String nacosRouteDataId) {
        GatewayConfig.nacosRouteDataId = nacosRouteDataId;
    }


    @Value("${nacos.gateway.route.config.group}")
    public  void setNacosRouteGroup(String nacosRouteGroup) {
        GatewayConfig.nacosRouteGroup = nacosRouteGroup;
    }
}

4.编写动态更新路由网关相关工具类

@RequiredArgsConstructor
@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {

    //写路由定义
    private final RouteDefinitionWriter routeDefinitionWriter;
    //获取路由定义
    private final RouteDefinitionLocator routeDefinitionLocator;


    //事件发布
    private ApplicationEventPublisher publisher;


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        //完成事件推送句柄的初始化
        this.publisher=applicationEventPublisher;
    }

    /**
     * 新增路由
     */
    public String addRouteDefinition(RouteDefinition definition){
        log.info("gateway add route : [{}]",definition);
        //保存路由配置并发布
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        //发布事件通知给Gateway,同步新增的路由定义
        this.publisher.publishEvent(new RefreshRoutesEvent(this));

        return "add success";

    }

    /**
     * 删除路由
     */
    public String deleteRouteById(String id){
        try {
            log.info("gateway delete route id:[{}]",id);
            routeDefinitionWriter.delete(Mono.just(id)).subscribe();
                    //发布事件通知给GateWay,同步更新路由定义
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "delete success";

        }catch (Exception ex){
            log.error("gateway delete route fail:[{}]", ex.getMessage(),ex);
            return "delete fail";

        }
    }

    /**
     * 更新路由:删除+新增=更新
     */
    public String updateByRouteDefinition(RouteDefinition definition){


        try {
            log.info("gateway update route:[{}]",definition);
            //只是删除不要去发布
            this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
        } catch (Exception e) {
            return "update fail,not find route routeId"+definition.getId();
        }


        try {
            //现在可以发布了
            this.routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception e) {
            return "update route fail";
        }
    }


    /**
     * 批量更新
     */
    public String updateList(List<RouteDefinition> definitions){

        log.info("gateway update route:[{}]",definitions);
        //先拿到当前gateway中存储的路由定义
        List<RouteDefinition> routeDefinitions = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
        if (!CollectionUtil.isEmpty(routeDefinitions)){
            //如果gateway中存在旧的路由定义,那么要清除掉
            routeDefinitions.forEach(rd->{
                log.info("delete route definition:[{}]",rd);
                deleteRouteById(rd.getId());
            });


        }
        //把更新的路由定义同步到gateway中
        definitions.forEach(definition -> updateByRouteDefinition(definition));

        return "success";

    }

}

可以看到,此类中,都是对网关存储的配置信息进行增删改查,以便我们读取到nacos的信息之后,对网关中的“旧数据”进行操作;

5.编写网关与nacos连接类

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
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.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 1. @ClassDescription:
 * 2. @author: Dongkoer
 * 3. @date: 2023年10月19日 15:45
 */
@Slf4j
@Component
@DependsOn({"gatewayConfig"})//依赖,要先等gatewayConfig先注入之后,自己才能注入
public class DynamicRouteServiceImplByNacos {

    //nacos配置服务
    private ConfigService configService;
    @Autowired
    DynamicRouteServiceImpl dynamicRouteService;




    @PostConstruct//bean初始化完成之后立马执行此方法
    public void init() {
        log.info("gateway route init...");


        try {
            //初始化Nacos配置客户端
            configService=initConfigService();
            if (null  == configService){
                log.error("init config service fail");
                return;

            }
            //通过Nacos Config 并指定路由配置路径去获取路由配置
            String config = configService.getConfig(
                    GatewayConfig.nacosRouteDataId,
                    GatewayConfig.nacosRouteGroup,
                    GatewayConfig.DEFAULT_TIMEOUT

            );

            log.info("get current gateway config :[{}]",config);
            List<RouteDefinition> definitionList = JSON.parseArray(config, RouteDefinition.class);


            if (CollectionUtil.isNotEmpty(definitionList)){
                for (RouteDefinition routeDefinition : definitionList) {
                    log.info("init gateWay config :[{}]",routeDefinition.toString());
                    dynamicRouteService.addRouteDefinition(routeDefinition);
                }
            }

        } catch (Exception e) {
            log.error("gateway route has some error:[{}]",e.getMessage(),e);
        }

        //设置监听器
        dynamicRouteByNacosListener(GatewayConfig.nacosRouteDataId,
                GatewayConfig.nacosRouteGroup);

    }

    /**
     * 连接nacos
     * @return
     */
    private ConfigService initConfigService() {
        Properties properties = new Properties();
        properties.setProperty("serverAddr", GatewayConfig.serverAddr);
        properties.setProperty("namespace", GatewayConfig.namespace);

        try {
            return configService = NacosFactory.createConfigService(properties);
        } catch (NacosException e) {
            log.error("init gateway nacos config error:[{}]", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 监听Nacos下发的动态路由配置
     *
     * @param dataId
     * @param group
     */
    private void dynamicRouteByNacosListener(String dataId, String group) {

        try {
            configService.addListener(dataId, group, new Listener() {
                //可以自定义线程池来操作
                @Override
                public Executor getExecutor() {
                    return null;
                }

                //此时传过来的s就是Nacos中最新的配置
                @Override
                public void receiveConfigInfo(String s) {
                    log.info("start to update config:[{}]", s);
                    List<RouteDefinition> definitionList = JSON.parseArray(s, RouteDefinition.class);
                    log.info("update route :[{}]", definitionList.toString());
                    dynamicRouteService.updateList(definitionList);

                }

            });
        } catch (NacosException e) {
            log.error("dynamic update gateway config error:[{}]", e.getMessage(), e);

        }


    }
}

6.对init方法的讲解

@PostConstruct//bean初始化完成之后立马执行此方法
    public void init() {
        log.info("gateway route init...");


        try {
            //初始化Nacos配置客户端
            configService=initConfigService();
            if (null  == configService){
                log.error("init config service fail");
                return;

            }
            //通过Nacos Config 并指定路由配置路径去获取路由配置
            String config = configService.getConfig(
                    GatewayConfig.nacosRouteDataId,
                    GatewayConfig.nacosRouteGroup,
                    GatewayConfig.DEFAULT_TIMEOUT

            );

            log.info("get current gateway config :[{}]",config);
            List<RouteDefinition> definitionList = JSON.parseArray(config, RouteDefinition.class);


            if (CollectionUtil.isNotEmpty(definitionList)){
                for (RouteDefinition routeDefinition : definitionList) {
                    log.info("init gateWay config :[{}]",routeDefinition.toString());
                    dynamicRouteService.addRouteDefinition(routeDefinition);
                }
            }

        } catch (Exception e) {
            log.error("gateway route has some error:[{}]",e.getMessage(),e);
        }

        //设置监听器
        dynamicRouteByNacosListener(GatewayConfig.nacosRouteDataId,
                GatewayConfig.nacosRouteGroup);

    }

在咱们的spring容器加载完bean之后,会立马执行此方法,其中,我们调用initConfigService()方法连接到Nacos,并获取到相应配置信息,完成configService的初始化

 try {
            //初始化Nacos配置客户端
            configService=initConfigService();
            if (null  == configService){
                log.error("init config service fail");
                return;

            }

然后我们通过congigservice中的本地nacos连接信息,获取到我们配置在nacos中对应的配置信息:

//通过Nacos Config 并指定路由配置路径去获取路由配置
            String config = configService.getConfig(
                    GatewayConfig.nacosRouteDataId,
                    GatewayConfig.nacosRouteGroup,
                    GatewayConfig.DEFAULT_TIMEOUT

            );

随后,我们对字符串的数据进行解析,如果能解析到数据,就说明我们的nacos中有相关配置信息,我们需要在网关中设置信息其中的addRouteDefiniton方法,就是将拉取到的信息,设置在网关中

  log.info("get current gateway config :[{}]",config);
            List<RouteDefinition> definitionList = JSON.parseArray(config, RouteDefinition.class);


            if (CollectionUtil.isNotEmpty(definitionList)){
                for (RouteDefinition routeDefinition : definitionList) {
                    log.info("init gateWay config :[{}]",routeDefinition.toString());
                    dynamicRouteService.addRouteDefinition(routeDefinition);
                }
            }

        } catch (Exception e) {
            log.error("gateway route has some error:[{}]",e.getMessage(),e);
        }

最后,我们配置监听器,如果此配置文件有改动,那么我们的网关配置信息跟着改动

 //设置监听器
        dynamicRouteByNacosListener(GatewayConfig.nacosRouteDataId,
                GatewayConfig.nacosRouteGroup);

    }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Mybatis查找返回Map,List集合类型的数据方式

    Mybatis查找返回Map,List集合类型的数据方式

    这篇文章主要介绍了Mybatis查找返回Map,List集合类型的数据方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 一篇文章彻底解决IDEA输出中文乱码问题

    一篇文章彻底解决IDEA输出中文乱码问题

    IDEA输出中文是乱码的问题,网上教程很多,很复杂,作者测试了很多种办法,现在将总结的方法提供给大家,下面这篇文章主要给大家介绍了关于彻底解决IDEA输出中文乱码问题的相关资料,需要的朋友可以参考下
    2023-05-05
  • 解决RestTemplate第一次请求响应速度较慢的问题

    解决RestTemplate第一次请求响应速度较慢的问题

    这篇文章主要介绍了解决RestTemplate第一次请求响应速度较慢的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Spring核心方法refresh的使用解析

    Spring核心方法refresh的使用解析

    这篇文章主要介绍了Spring核心方法refresh的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • 你应该知道的java中的5个注解

    你应该知道的java中的5个注解

    自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分。开发过程中,我们也时常在应用代码中会看到像@Override,@Deprecated这样的注解。下面小编和大家来一起学习一下吧
    2019-05-05
  • 使用JSONObject.toJSONString 过滤掉值为空的key

    使用JSONObject.toJSONString 过滤掉值为空的key

    这篇文章主要介绍了使用JSONObject.toJSONString 过滤掉值为空的key,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • LoggingEventAsyncDisruptorAppender类执行流程源码解读

    LoggingEventAsyncDisruptorAppender类执行流程源码解读

    这篇文章主要介绍了LoggingEventAsyncDisruptorAppender类执行流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Spring条件注解用法案例分析

    Spring条件注解用法案例分析

    这篇文章主要介绍了Spring条件注解用法,结合具体实例形式分析了Spring条件注解相关原理、使用方法及操作注意事项,需要的朋友可以参考下
    2019-11-11
  • Spring Boot 注解体系与工程实践示例指南

    Spring Boot 注解体系与工程实践示例指南

    本文详细介绍了SpringBoot核心注解的语法、语义、适用场景及组合方式,涵盖了自动配置、条件化注入、AOP与事务等关键领域,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • 一篇文章细数Java中List的10个坑

    一篇文章细数Java中List的10个坑

    在Java中List是一个有序集合,允许存储重复的元素,它是Java集合框架的一部分,提供了对集合进行各种操作的接口,这篇文章主要介绍了Java中List10个坑的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-11-11

最新评论