springcloud gateway扩展支持多版本灰度

改造要求

需要在原有的调度策略中通过客户端header中的version进行1个服务多实例下进行二次分组,让指定的version在指定的版本实例下进行轮训调度。

需要改造的点

1.业务服务在发布到naocs中的元数据需要指定版本号

2.网关的调度策略中需要增加版本的区分

3.无版本的header。默认还继续使用轮训调度

4.使用jenkins进行发布服务的时候需要允许发布多个服务,

5.nacos中体现的形式是一个服务多个实例,只是元数据不同而已。

开始改造

在业务服务中的bootstrap.yml中指定version

gateway的改造

1.先定义好自己的LoadBanlancer

public class EnhancedRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private final String serviceId;
    private final AtomicInteger position;

    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    public EnhancedRoundRobinLoadBalancer(String serviceId,
                                          ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
        this.serviceId = serviceId;
        this.position = new AtomicInteger(new Random().nextInt(1000));
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next().map((serviceInstances) -> {
            return this.processInstanceResponse(request, supplier, serviceInstances);
        });

    }

    /**
     * copy自 RoundRobinLoadBalancer
     *
     * @param request
     * @param supplier
     * @param serviceInstances {@link  RoundRobinLoadBalancer}
     * @return
     */
    private Response<ServiceInstance> processInstanceResponse(Request request,
                                                              ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances) {
//        log.info("-----------------------------------------------------");
//        log.info("获取到的实例个数:{}", serviceInstances.size());
        //根据header头携带version进行同一个服务下实例过滤,过滤后再使用roundRobinInstance轮训
        String headerVersion = getRequestHeader(request, "version");
//        log.info("获取到的version:{}", headerVersion);
        if (!ObjectUtils.isEmpty(headerVersion)){
            ArrayList<ServiceInstance> versionServiceInstances = new ArrayList<>();
            for (ServiceInstance serviceInstance : serviceInstances) {
                Map<String, String> metadata = serviceInstance.getMetadata();
                String version = metadata.get("version");
                if (!ObjectUtils.isEmpty(version) && headerVersion.equals(version)){
                    versionServiceInstances.add(serviceInstance);
                }
            }
            //带版本的实例不为空的情况下,使用版本实例进行轮训
            if (!ObjectUtils.isEmpty(versionServiceInstances)){
//                log.info("使用版本实例轮训,携带版本实例个数:{}",versionServiceInstances.size());
                Response<ServiceInstance> serviceInstanceResponse = getRoundRobinInstance(versionServiceInstances);
                if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
                    ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
                }
                return serviceInstanceResponse;
            }
        }



        Response<ServiceInstance> serviceInstanceResponse = getRoundRobinInstance(serviceInstances);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }


    /**
     * 使用RoundRobin机制获取节点
     *
     * @param instances 实例
     * @return {@link Response }<{@link ServiceInstance }>
     * @author : baohui
     */
    private Response<ServiceInstance> getRoundRobinInstance(List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("不存在可用的服务: " + serviceId);
            }
            return new EmptyResponse();
        } else if (instances.size() == 1) {
            return new DefaultResponse(instances.get(0));
        } else {
            // 每一次计数器都自动+1,实现轮询的效果
            int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
            ServiceInstance instance = instances.get(pos % instances.size());
            return new DefaultResponse(instance);
        }
    }

    private String getRequestHeader(Request request, String headerField) {
        HttpHeaders headers = ((RequestDataContext) request.getContext()).getClientRequest().getHeaders();
        //log.info("headers:{}", headers);
        return headers.getFirst(headerField);


    }
jenkins调整

调整jenkins发布pod的相关参数

1. 在selector和template中增加version key 

2. 调整meta-data中的name生成机制。之所以需要调整name。是为了在k8s集群中可以让这两个服务共存。

nacos中的效果

写在最后

1.此方案是可以满足单独一个服务的的多版本共存,但是老的服务需要手动在集群中下线

2.涉及到多个服务跨服务调用的情况下需要根据跨服务的中间件单独处理。例如 dubbo的话需要调整dubbo调用的version参数。这样才能满足新旧服务的调用隔离。

3.涉及到http客户端调用需要中转到网关后并在请求参数中增加version才能达到隔离的效果

相关推荐

  1. SpringCloudGateway

    2024-06-06 12:06:07       25 阅读
  2. SpringCloudGateway 3.1.4版本 Netty内存泄漏问题解决

    2024-06-06 12:06:07       62 阅读
  3. C#类库打包支持版本的类库

    2024-06-06 12:06:07       31 阅读
  4. 15. 扩展: Spring Boot CORS支持

    2024-06-06 12:06:07       45 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-06 12:06:07       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-06 12:06:07       101 阅读
  3. 在Django里面运行非项目文件

    2024-06-06 12:06:07       82 阅读
  4. Python语言-面向对象

    2024-06-06 12:06:07       91 阅读

热门阅读

  1. 前端 、Python 扁平化嵌套数组和应用场景

    2024-06-06 12:06:07       27 阅读
  2. 如何搭建开发一款看广告赚钱软件?

    2024-06-06 12:06:07       27 阅读
  3. spring整合kafka

    2024-06-06 12:06:07       23 阅读
  4. go语言接口之接口类型

    2024-06-06 12:06:07       33 阅读
  5. 爬山算法的详细介绍

    2024-06-06 12:06:07       34 阅读
  6. spring 事务失效的几种场景

    2024-06-06 12:06:07       26 阅读
  7. C#的线程锁

    2024-06-06 12:06:07       28 阅读
  8. django连接达梦数据库

    2024-06-06 12:06:07       25 阅读
  9. Lua使用方式介绍

    2024-06-06 12:06:07       27 阅读
  10. k8s中pod如何排错?

    2024-06-06 12:06:07       24 阅读
  11. C++学习笔记“类和对象”;运算符重载

    2024-06-06 12:06:07       32 阅读
  12. 二叉树的层平均值-力扣

    2024-06-06 12:06:07       31 阅读
  13. 功能强大的开源数据中台系统 DataCap 2024.03.5 发布

    2024-06-06 12:06:07       21 阅读
  14. SQL小白10分钟快速入门

    2024-06-06 12:06:07       33 阅读