SpringBoot+Nacos+Gateway动态路由

前言:
动态路由: 可见标题 架构选择nacos作为服务注册,GateWay作为网关两者都是ali。那nacos咱们都知道可作为配置中心
来使用且可动态,那做一个动态路由我觉得还是有些必要性的,不至于每次增加路由都要重新部署服务。之前写过一篇动态路由的文章,但那个可能使用的cloud,springboot版本低 以至于到现在的版本监听不到,而且那个是json版本。所以写下这篇文章。

‘新版’动态路由(yaml格式)

版本信息:springboot:2.2.2.RELEASE------com.alibaba.cloud: 2.2.4.RELEASE-----org.springframework.cloud:Hoxton.RELEASE

上代码

@Data
@Component
@ConfigurationProperties(prefix = "broad.dynamic.route")//yml配置信息,下文给出
@ConditionalOnBean(DynamicRouteConfiguration.class)
public class DynamicRouteProperties {
    /**
     * nacos 配置管理  dataId
     */

    private String dataId;
    /**
     * nacos 配置管理 group
     */

    private String group;
    /**
     * nacos 服务地址
     */

    private String ipAddr;

    /**
     * 启动动态路由的标志,默认关闭
     */

    private boolean enabled = false;
}

DynamicRouteConfiguration

@Configuration
@ConditionalOnProperty(name = "broad.dynamic.route.enabled", matchIfMissing = true)
public class DynamicRouteConfiguration implements ApplicationEventPublisherAware {
    Logger log= LoggerFactory.getLogger(DynamicRouteConfiguration.class);

    //namespace名称
    private final static String NAMESPACE="8359a8d9-03ab-4574-9627-9456351c0c01";
    //nacos账号密码
    private final static String USERNAME_PASSWORD="nacos";
    //截取字符
    private final static String REPLACE="routes:";

    private DynamicRouteProperties bean;
    private RouteDefinitionWriter writer;

    private ApplicationEventPublisher publisher;

    public DynamicRouteConfiguration(DynamicRouteProperties bean, RouteDefinitionWriter writer, ApplicationEventPublisher publisher) {
        this.bean = bean;
        this.writer = writer;
        this.publisher = publisher;
    }

    @PostConstruct
    private void init() {
        Assert.notNull(bean.getDataId(), "broad.dynamic.route.dataId null异常");
        Assert.notNull(bean.getGroup(), "broad.dynamic.route.group is null异常");
        Assert.notNull(bean.getIpAddr(), "broad.dynamic.route.ipAddr is null异常");
        dynamicRouteByNacosListener();
    }

    private void dynamicRouteByNacosListener() {
        try {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE);
            properties.put(PropertyKeyConst.SERVER_ADDR, bean.getIpAddr());
            properties.put("username",USERNAME_PASSWORD);
            properties.put("password",USERNAME_PASSWORD);
            ConfigService configService = NacosFactory.createConfigService(properties);
            String content = configService.getConfigAndSignListener(
                    bean.getDataId(),
                    bean.getGroup(),
                    5000,
                    new AbstractListener() {
                        @Override
                        public void receiveConfigInfo(String configInfo) {
                            updateConfig(configInfo);
                        }
                    });

            updateConfig(content);
        } catch (NacosException e) {
            log.error("nacos 获取动态路由配置和监听异常", e);
        }
    }

    private void updateConfig(String content) {
        String contentReally=replaceContext(content);
        log.info("nacos 动态路由更新: {}", contentReally);
        try {
            getRouteDefinitions(contentReally).forEach(routeDefinition -> {
                log.info("动态路由配置: {}", routeDefinition);
                writer.delete(Mono.just(routeDefinition.getId()));
                writer.save(Mono.just(routeDefinition)).subscribe();
                publisher.publishEvent(new RefreshRoutesEvent(this));

            });
        } catch (Exception e) {
            log.error("更新动态路由配置异常: ", e);
        }
    }

    /**
     * 因在nacos配置路由携带 spring前缀,会导致yaml解析失败,因此需截取
     * @return
     */
    private String replaceContext(String content){
        String contentDelete=content.replaceAll("\r|\n", "").substring(0, content.indexOf(REPLACE)+1);
        String contentReally=content.substring(contentDelete.length()+7, content.length());
        return  contentReally;
    }

    /**
     * yaml转换
     * @param content
     * @return
     */
    private List<RouteDefinition> getRouteDefinitions(String content) {
        return JSON.parseArray(JSON.toJSONString(
                YamlHelper.getInstance().load(content)
        ), RouteDefinition.class);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }
}

yaml

public abstract class YamlHelper {
    private YamlHelper() {}
    public static Yaml getInstance() {
        return InnerClass.YAML;
    }

    private static class InnerClass {
        private static final Yaml YAML = new Yaml();
    }
}

解释:

第1:

但是在转换之前字符串content格式不正确,因为我在nacos配置的还有前缀 如下图:

springgateway 动态路由Nacos springboot 动态路由_gateway

(但好像不加这个前缀也可以,这个我忘试了 大家可以自测一下)
所以我需要把这些前缀去掉。因此有了去除前缀的方法replaceContext

第2:

springgateway 动态路由Nacos springboot 动态路由_spring_02

第三:这是yml配置的参数

springgateway 动态路由Nacos springboot 动态路由_spring_03

都是干货呦,大家有什么不懂可以评论区问我,有写的不对的大家指正,感谢!