实现springCloud GateWay 结合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);

    }

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nacos是一个分布式的服务注册和发现系统。Spring Cloud Gateway是开源的网关,可以实现动态路由结合NacosSpring Cloud Gateway可以实现动态路由的功能。 下面是通过Nacos实现Spring Cloud Gateway动态路由的步骤: 1. 添加Nacos组件。在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> ``` 2. 实现Spring Cloud Gateway注册到Nacos的功能。 在application.yml文件中添加以下配置: ``` spring: application: name: gateway-service cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true ``` 其中,将enabled设置为true,表示启用Spring Cloud Gateway的服务发现功能;将lower-case-service-id设置为true,表示服务名全部转换为小写。 3. 实现动态路由。 在Nacos中创建config和route两个配置。config配置用来存放动态路由信息,route配置用来存放每个服务的路由信息。 在route配置中添加以下配置: ``` spring: cloud: gateway: routes: - id: user-service uri: loadbalancer://user-service predicates: - Path=/users ``` 其中,id表示服务名,uri表示服务的负载均衡地址,predicates表示路由谓语,可以是Path、Query等。 在config配置中添加以下配置: ``` spring: cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true routes: - id: user-service predicates: - Path=/api/users/** filters: - StripPrefix=1 uri: lb://user-service metadata: version: 1.0 ``` 其中,id表示服务名,predicates表示路由谓语,filters表示过滤器,StripPrefix表示去掉前缀的过滤器,uri表示服务的负载均衡地址,metadata表示元数据信息。 通过以上配置,就可以实现NacosSpring Cloud Gateway动态路由的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值