Spring Cloud Alibaba - 12 使用Nacos的元数据实现金丝雀发布功能

Posted 小小工匠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud Alibaba - 12 使用Nacos的元数据实现金丝雀发布功能相关的知识,希望对你有一定的参考价值。

文章目录


需求

新功能要上线了 , order-center 存在二个版本 V1(老版本) V2(新版本),product-center也存在二个版本V1(老版本) V2新版本 现在需要做到的是order-center(V1)---->product-center(v1),order-center(V2)---->product-center(v2)

v2版本是小面积部署的,用来测试用户对新版本功能的。若用户完全接受了v2。我们就可以把V1版本卸载完全部署V2版本。

如下调用关系

改造

Spring Cloud Alibaba - 11 Ribbon 自定义负载均衡策略(同集群优先权重负载均衡算法) 在这个基础上实现该功能

自定义规则

package com.artisan.customrules;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;


/**
 * @author 小工匠
 * @version 1.0
 * @description: 金丝雀版本权重负载均衡策略
 * @date 2022/2/3 13:43
 * @mark: show me the code , change the world
 */

@Slf4j
public class SameClusterPriorityWithVersionRule extends AbstractLoadBalancerRule 

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) 

    

    @Override
    public Server choose(Object key) 

        try 

            //获取本地所部署集群的名称 NJ-CLUSTER
            String localClusterName = discoveryProperties.getClusterName();

            //去nacos上获取和本地 相同集群   相同版本的所有实例信息
            List<Instance> theSameClusterNameAndTheSameVersionInstList = getTheSameClusterAndTheSameVersionInstances(discoveryProperties);

            //声明被调用的实例
            Instance toBeChooseInstance;

            //判断同集群同版本号的微服务实例是否为空
            if(theSameClusterNameAndTheSameVersionInstList.isEmpty()) 
                //跨集群调用相同的版本
                toBeChooseInstance = crossClusterAndTheSameVersionInovke(discoveryProperties);
            else 
                //具有同集群  同版本号的实例
                toBeChooseInstance = ArtisanWeightedBalancer.chooseInstanceByRandomWeight(theSameClusterNameAndTheSameVersionInstList);
                log.info("同集群同版本调用--->当前微服务所在集群:,被调用微服务所在集群:,当前微服务的版本:,被调用微服务版本:,Host:,Port:",
                        localClusterName,toBeChooseInstance.getClusterName(),discoveryProperties.getMetadata().get("current-version"),
                        toBeChooseInstance.getMetadata().get("current-version"),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
            
            return new NacosServer(toBeChooseInstance);
         catch (NacosException e) 
            log.error("同集群优先权重负载均衡算法选择异常:",e);
            return null;
        
    



    /**
     * 方法实现说明:获取相同集群下,相同版本的 所有实例
     * @author:smlz
     * @param discoveryProperties nacos的配置
     * @return: List<Instance>
     * @exception: NacosException
     */
    private List<Instance> getTheSameClusterAndTheSameVersionInstances(NacosDiscoveryProperties discoveryProperties) throws NacosException 

        //当前的集群的名称
        String currentClusterName = discoveryProperties.getClusterName();

        //当前的版本号
        String currentVersion = discoveryProperties.getMetadata().get("current-version");

        //获取所有实例的信息(包括不同集群的,不同版本号的)
        List<Instance> allInstance =  getAllInstances(discoveryProperties);

        List<Instance> theSameClusterNameAndTheSameVersionInstList = new ArrayList<>();

        //过滤相同集群  同版本号的实例
        for(Instance instance : allInstance) 
            if(StringUtils.endsWithIgnoreCase(instance.getClusterName(),currentClusterName)&&
               StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"),currentVersion)) 

                theSameClusterNameAndTheSameVersionInstList.add(instance);
            
        

        return theSameClusterNameAndTheSameVersionInstList;
    

    /**
     * 方法实现说明:获取被调用服务的所有实例
     * @author:smlz
     * @param discoveryProperties nacos的配置
     * @return: List<Instance>
     * @exception: NacosException
     */
    private List<Instance> getAllInstances(NacosDiscoveryProperties discoveryProperties) throws NacosException 

        //第1步:获取一个负载均衡对象
        BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) getLoadBalancer();

        //第2步:获取当前调用的微服务的名称
        String invokedSerivceName = baseLoadBalancer.getName();

        //第3步:获取nacos clinet的服务注册发现组件的api
        NamingService namingService = discoveryProperties.namingServiceInstance();

        //第4步:获取所有的服务实例
        List<Instance> allInstance =  namingService.getAllInstances(invokedSerivceName);

        return allInstance;
    

    /**
     * 方法实现说明:跨集群环境下 相同版本的
     * @author:smlz
     * @param discoveryProperties
     * @return: List<Instance>
     * @exception: NacosException
     */
    private List<Instance> getCrossClusterAndTheSameVersionInstList(NacosDiscoveryProperties discoveryProperties) throws NacosException 

        //版本号
        String currentVersion = discoveryProperties.getMetadata().get("current-version");

        //被调用的所有实例
        List<Instance> allInstance = getAllInstances(discoveryProperties);

        List<Instance>  crossClusterAndTheSameVersionInstList = new ArrayList<>();

        //过滤相同版本
        for(Instance instance : allInstance) 
            if(StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"),currentVersion)) 

                crossClusterAndTheSameVersionInstList.add(instance);
            
        

        return crossClusterAndTheSameVersionInstList;
    

    private Instance crossClusterAndTheSameVersionInovke(NacosDiscoveryProperties discoveryProperties) throws NacosException 

        //获取所有集群下相同版本的实例信息
        List<Instance>  crossClusterAndTheSameVersionInstList = getCrossClusterAndTheSameVersionInstList(discoveryProperties);
        //当前微服务的版本号
        String currentVersion = discoveryProperties.getMetadata().get("current-version");
        //当前微服务的集群名称
        String currentClusterName = discoveryProperties.getClusterName();

        //声明被调用的实例
        Instance toBeChooseInstance = null ;

        //没有对应相同版本的实例
        if(crossClusterAndTheSameVersionInstList.isEmpty()) 
            log.info("跨集群调用找不到对应合适的版本当前版本为:currentVersion:",currentVersion);
            throw new RuntimeException("找不到相同版本的微服务实例");
        else 
            toBeChooseInstance = ArtisanWeightedBalancer.chooseInstanceByRandomWeight(crossClusterAndTheSameVersionInstList);

            log.info("跨集群同版本调用--->当前微服务所在集群:,被调用微服务所在集群:,当前微服务的版本:,被调用微服务版本:,Host:,Port:",
                    currentClusterName,toBeChooseInstance.getClusterName(),discoveryProperties.getMetadata().get("current-version"),
                    toBeChooseInstance.getMetadata().get("current-version"),toBeChooseInstance.getIp(),toBeChooseInstance.getPort());
        

        return toBeChooseInstance;
    


全局规则配置

@Configuration
public class GlobalRibbonConfig 

    @Bean
    public IRule globalConfig() 
        // 根据权重的规则
        // return new ArtisanWeightedRule();

        // 同集群优先调用规则
        // return new SameClusterPriorityRule();

        // 金丝雀版本权重负载均衡策略
        return new SameClusterPriorityWithVersionRule();
    


配置文件


ORDER    --------  BeiJingCluster      V1 

PRODUCT  --------  BeiJingCluster      V1 
PRODUCT  --------  BeiJingCluster      V2
PRODUCT  --------  GuangDongCluster    V1 
PRODUCT  --------  GuangDongCluster    V2


artisan-cloud-customcfg-ribbon-order工程中Nacos的配置如下:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 1.117.97.88:8848
        cluster-name: BeiJingCluster
        metadata:
          current-version: V1

  application:
    name: artisan-order-center
server:
  port: 8080

服务启动后


artisan-cloud-customcfg-ribbon-product工程中Nacos的配置如下:

【BeiJingCluster V1】

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 1.117.97.88:8848
        cluster-name: BeiJingCluster
        metadata:
          current-version: V1

  application:
    name: artisan-product-center
server:
  port: 7777

【BeiJingCluster V2】

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 1.117.97.88:8848
        cluster-name: BeiJingCluster
        metadata:
          current-version: V2

  application:
    name: artisan-product-center
server:
  port: 7778

【GuangDongCluster V1】

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 1.117.97.88:8848
        cluster-name: GuangDongCluster
        metadata:
          current-version: V1

  application:
    name: artisan-product-center
server:
  port: 7779

【GuangDongCluster V2】

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 1.117.97.88:8848
        cluster-name: GuangDongCluster
        metadata:
          current-version: V2

  application:
    name: artisan-product-center
server:
  port: 7780


验证

访问Order提供的接口,

观察访问日志

2022-02-03 19:06:32.791  INFO 1856 --- [nio-8080-exec-1] c.a.c.SameClusterPriorityWithVersionRule : 同集群同版本调用--->当前微服务所在集群:BeiJingCluster,被调用微服务所在集群:BeiJingCluster,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.5.1,Port:7777

再看看Product 7777

测试结果来看order调用product的时候 优先会调用同集群同版本的product提供的服务。

紧接着,我们把同集群的 同版本的product-center下线,那们就会发生跨集群调用相同的版本

再次调用, 观察日志

2022-02-03 19:10:24.754  INFO 1856 --- [nio-8080-exec-4] c.a.c.SameClusterPriorityWithVersionRule : 跨集群同版本调用--->当前微服务所在集群:BeiJingCluster,被调用微服务所在集群:GuangDongCluster,当前微服务的版本:V1,被调用微服务版本:V1,Host:192.168.5.1,Port:7779

说明了当当前集群不存在相同版本的服务时,会跨集群调用相同的版本 。


当我们把Order 也搞个V2 版本


再次调用

观察日志


源码

https://github.com/yangshangwei/SpringCloudAlibabMaster/tree/master

以上是关于Spring Cloud Alibaba - 12 使用Nacos的元数据实现金丝雀发布功能的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud Alibaba 2.2.6发布:新增Nacos注册快速失败的配置

spring cloud alibaba 组件版本关系 以及 毕业版本依赖关系

spring cloud alibaba nacos搭建最小可运行微服务

Spring Cloud Alibaba环境搭建

Spring Cloud Alibaba全家桶——Spring Cloud Alibaba介绍

Spring Cloud Alibaba 2.2.6发布:新增Nacos注册快速失败的配置