Nacos配置gateway动态路由

网关的路由配置在项目启动时加载,并存到内存中的路由表内(一个Map),不会改变。也不会监听路由变更,因此,需要监听Nacos的配置变更,手动更新路由表。

1.引入依赖

<!--统一配置管理-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--加载bootstrap-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

2.编写配置文件

在网关gatewayresources目录创建bootstrap.yaml文件,内容如下:

spring:
  application:
    name: gateway    # 你的服务名字
  cloud:
    nacos:
      server-addr: 192.168.230.131
      config:
        file-extension: yaml
        shared-configs:
          - dataId: shared-log.yaml # 共享日志配置

3.编写动态路由文件

将原本yaml中配置的路由信息删除,在nacos中创建一个新的用于动态路由的文件,使用json的原因是,当我们监听到配置变更后,拉取新的配置文件,返回格式是字符串,在java中json格式的好解析成对象

内容如下:

是一个RouteDefinition数组,其中id和uri跟之前的长相类似,但注意路由断言,也是是一个list格式的,里面存的是断言的对象,由名字(规则类型)和参数组成,这里的路径参数,要按照格式写

"args": {"_genkey_0":"请求格式路径1", "_genkey_1":"请求格式路径2",, "_genkey_2":"以此类推"}

[
    {
        "id": "item",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
        }],
        "filters": [],
        "uri": "lb://item-service"
    },
    {
        "id": "cart",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/carts/**"}
        }],
        "filters": [],
        "uri": "lb://cart-service"
    }
    
]

4.编写配置监听器

nacosConfigManager.getConfigService() 得到的 ConfigService 实例可以用来从 Nacos 服务器获取配置信息

我们需要在启动服务时候拉取路由信息,并在配置变化时候更新信息,所以可以使用

ConfigService.getConfigAndSignListener(配置文件id,group,超时时间,监听器)方法返回字符串格式的配置文件

RouteDefinitionWriter 是一个接口,用于动态地定义和更新路由信息,允许你在运行时添加、删除或修改路由规则,而不需要重启应用程序。

我们获得了配置文件的字符串形式,可以解析成路由对象,并使用RouteDefinitionWriter 对路由信息进行修改

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Slf4j
@Component
public class DynamicRouteLoader {
    @Autowired
    private  RouteDefinitionWriter writer;  //用来修改路由
    @Autowired
    private  NacosConfigManager nacosConfigManager;  //获取与nacos的连接

    // 路由配置文件的id和分组
    private final String dataId = "gateway-routes.json";
    private final String group = "DEFAULT_GROUP";
    // 保存更新过的路由id
    private final Set<String> routeIds = new HashSet<>();

    @PostConstruct //bean被实例化之后,Spring容器会立即调用带有@PostConstruct 注解的方法
    public void initRouteConfigListener() throws NacosException {
        // 1.注册监听器并首次拉取配置
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //更新配置
                        updateConfigInfo(configInfo);
                    }
                });

        // 2.首次启动时,更新一次配置
        updateConfigInfo(configInfo);
    }

    private void updateConfigInfo(String configInfo) {
        log.debug("监听到路由配置变更,{}", configInfo);
        // 1.解析配置信息,转为RouteDefinition
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        // 2.更新前先清空旧路由
        // 2.1.清除旧路由
        for (String routeId : routeIds) {
            writer.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();
        // 2.2.判断是否有新的路由要更新
        if (CollectionUtil.isEmpty(routeDefinitions)) {
            // 无新路由配置,直接结束
            return;
        }
        // 3.更新路由
        routeDefinitions.forEach(routeDefinition -> {
            // 3.1.更新路由
            writer.save(Mono.just(routeDefinition)).subscribe();
            // 3.2.记录路由id,方便将来删除
            routeIds.add(routeDefinition.getId());
        });
    }
}

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值