springcloud 通过远端配置文件实时刷新动态路由(适用于开发环境)

每个公司项目的情况都不一样,我这个只适用于zuul

然后我是根据配置文件去动态刷新路由的,看过网上另一个教程

https://blog.csdn.net/u013815546/article/details/68944039

一些相关知识可以了解一下,我就不做赘述了,大家可以先去看一下,了解一下基础的东西, 他的教程只适用于已存在的路由进行更新, 但是相关知识的介绍还是可以看看的

具体原因可以看 locateRoutes 方法,通过这个方法获取路由信息

protected Map<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap();
        Iterator var2 = this.properties.getRoutes().values().iterator();

        while(var2.hasNext()) {
            ZuulRoute route = (ZuulRoute)var2.next();
            routesMap.put(route.getPath(), route);
        }

        return routesMap;
    }

这是新增一个LinkedHashMap给用户, 所以往里面新增路由信息时,zuul是不会增加路由信息的,这就是他的动态路由的只能刷新部分路由的原因

接下来开始讲解我的方法,我是根据远端的配置文件实时更新路由信息的,大家可以根据自己的情况选择数据库或者其它之类的进行修改

创建 DynamicZuulConfig ,主要是为了实时获取路由信息, 关键注解 @RefreshScope , 用于刷新配置信息,具体作用自行度娘

package com.xxx.gate.customer.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Data
@RefreshScope
@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "developer")
public class DynamicZuulConfig {
    private boolean flag; // 标识刷新的flag
    private List<String> names;
    private List<String> gate-x; // 需要做动态路由的网关名称
}

创建 DynamicZuulConfiguration 用于将一些Bean对象注入到动态路由加载器中,

这边routeLocator方法加RefreshScope 注解是为了提醒zuul加载器,配置文件更新了, 参考 ↓

https://www.cnblogs.com/yjmyzz/p/8085530.html 

package com.xxx.gate.customer.config;

import com.xxx.gate.customer.locator.DynamicRouteLocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DynamicZuulConfiguration {
    @Autowired
    private DynamicZuulConfig developerNamesConfig; // 动态路由信息配置文件
    @Autowired
    private ZuulProperties zuulProperties;
    @Autowired
    private ServerProperties server;

    @Bean
    @RefreshScope //关键注解
    public DynamicRouteLocator routeLocator() {
        DynamicRouteLocator routeLocator = 
new DynamicRouteLocator(this.server.getServletPrefix(), this.zuulProperties, this.developerNamesConfig);
        return routeLocator;
    }
}

创建 DynamicRouteLocator 并继承 SimpleRouteLocator 和实现 RefreshableRouteLocator 接口

这才是主要的zuul加载路由信息的实现方法

private void locateRoutesRefresh用于封装动态路由信息, 关键是最后一句, 将路由信息重新设置到 ZuulProperties 中

properties.setRoutes(routeMap);
package com.xxx.gate.customer.locator;

import com.xxx.gate.customer.config.DynamicZuulConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;

import java.util.List;
import java.util.Map;

public class DynamicRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
    private static Logger logger = LoggerFactory.getLogger(DynamicRouteLocator.class);

    private DynamicZuulConfig developerNamesConfig;

    private ZuulProperties properties;

    public DynamicRouteLocator(String servletPath, ZuulProperties properties, DynamicZuulConfig developerNamesConfig) {
        super(servletPath, properties);
        this.properties = properties;
        this.developerNamesConfig = developerNamesConfig;
    }

    @Override
    public void refresh() {
        doRefresh();
    }

    @Override
    protected Map<String, ZuulRoute> locateRoutes() {
        // 从application.properties中加载路由信息
        Map<String, ZuulRoute> routeMap = properties.getRoutes();
        // 重新加载动态配置中信息
        locateRoutesRefresh(routeMap);
        return routeMap;
    }

    /**
     * 修改路由对象信息
     */
    private void locateRoutesRefresh(Map<String, ZuulRoute> routeMap) {
        if (null == developerNamesConfig || !developerNamesConfig.isFlag() || developerNamesConfig.getGate-X().isEmpty() || developerNamesConfig.getNames().isEmpty()) {
            return;
        }
        List<String> keys = developerNamesConfig.getGate-X();
        List<String> names = developerNamesConfig.getNames();
        keys.forEach(key ->
                names.forEach(name -> {
                    String id = key + "_" + name;
                    String path = "/" + id + "/**";
                    String serviceId = id.replaceAll("_", "-");
                    ZuulRoute zuulRoute = routeMap.get(path);
                    if (null == zuulRoute) {
                        zuulRoute = new ZuulRoute();
                    }
                    zuulRoute.setId(id);
                    zuulRoute.setPath(path);
                    zuulRoute.setServiceId(serviceId);
                    zuulRoute.setStripPrefix(true);
                    routeMap.put(path, zuulRoute);
                })
        );
        properties.setRoutes(routeMap);    //关键步骤,将路由信息重新设置到ZuulProperties中
        developerNamesConfig.setFlag(false);    //将更新标识设置为false
    }
}

以上就是所有代码,接下来讲解配置文件

在远端配置git上创建common_developer.yml 用于填写动态信息, 因为locateRoutes 方法会被一直调用,所以需要使用flag来标识需要更新路由信息,否则会一直重复对路由赋值

# 开发环境动态路由配置信息
developer:
    flag: true # 标识需要更新
    gate-a: # 网关A
        - a_microservice # a微服务
        - b_microservice # b微服务
        - c_microservice # c微服务
       

    gate-b: # 网关B
        - d_microservice # d微服务
        - e_microservice # e微服务

    gate-c: # 网关C
        - f_microservice # f微服务

    names:
        - zhangsan # 开发人员张三
        - lisi # 开发人员李四
        - wangwu #开发人员王五
        

这边可以针对每个网关都写一个配置文件,也可以针对所有网关和微服务写一个公共的配置文件

接下来是生成的格式

zuul路由关键属性是 id, path, serviceId, StripPrefix属性, 分别对应以下属性

id: zuul.routes.xxx 

path: zuul.routes.xxx.path // url路径

serviceId: zuul.routes.xxx.serviceId // 对应spring.application.name属性

StripPrefix: 默认为true

所以在设置spring.application.name 属性时最好和id, path, serviceId 统一

比如xxx-microservice,这样生成格式就是

zuul:
    routes:
        xxx_microservice:
            path: /xxx_microservice/**
            serviceId: xxx-microservice
        # 下面是动态路由生成的结构
        xxx_microservice_zhangsan:
            path: /xxx_microservice_zhangsan/**
            serviceId: xxx-microservice-zhangsan

这样就不用做多余的兼容代码了

要使用RefreshScope 需要引入下面的jar包 

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

然后在配置文件中加入下面的配置, 这是为了在调用refresh接口时忽略安全校验(好像是这个...),否则就无法触发@RefreshScope

management:
  security:
    enabled: false

最后就是通过webhooks功能实现远端配置文件更新时请求springcloud的refresh接口, 我这边用的是公司内部的gitlab

进入项目,然后在左侧选择 Setting -> integrations

URL 就是请求的地址,默认使用POST请求, 输入http://ip:port/refresh ,然后 Add webhook, 就可以添加了

之后每次更新配置中心文件时, 就会去post 网关的/rerfesh接口,然后刷新配置信息

如果帮到你,请点个赞吧 O(∩_∩)O~

猜你喜欢

转载自blog.csdn.net/qq171563857/article/details/84843254