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

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: Spring Cloud Alibaba - 12 使用Nacos的元数据实现金丝雀发布功能

6735aa4777de402592fbe82e8b40ee3d.png

需求


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


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


如下调用关系


3cc85ce23ea04d4cb38d47655b7427f9.png


改造


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


自定义规则

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


服务启动后


a40466f3aed746f39cd77f637f6b0737.png

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



5e045aaa37ba4cbbadbff23beb62907a.png

验证

访问Order提供的接口,

df39f3e8fd7649dd84a64cfda7d97ddf.png

观察访问日志

2022-02-03 19:06:32.791  INFO 1856 --- [nio-8080-exec-1] c.a.c.SameClusterPriorityWithVe


再看看Product 7777


58b9e2528a304870bfde1be8c85ad619.png


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

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

28bd425c1ff644fabb8de8520af9fbe4.png

2c801ff7e11c48f4a54ad8bb1d0999c8.png

再次调用, 观察日志

2022-02-03 19:10:24.754  INFO 1856 --- [nio-8080-exec-4] c.a.c.SameClusterPriorityWithVe


af6ea339a3af41b2870cb1050c0d6b3d.png

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


当我们把Order 也搞个V2 版本


8e7120acdb0f4f8a92a46c335d64e5bd.png

再次调用


4cf4ffb973384b028bccafc60b49fa5c.png

观察日志

481ee914d92f4e52be3e25c457424433.png


源码

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


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
26天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
13天前
|
存储 人工智能 Java
Spring AI Alibaba 配置管理,用 Nacos 就够了
本文通过一些实操案例展示了 Spring AI Alibaba + Nacos 在解决 AI 应用中一系列复杂配置管理挑战的方案,从动态 Prompt 模板的灵活调整、模型参数的即时优化,到敏感信息的安全加密存储。Spring AI Alibaba 简化了对接阿里云通义大模型的流程,内置 Nacos 集成也为开发者提供了无缝衔接云端配置托管的捷径,整体上极大提升了 AI 应用开发的灵活性和响应速度。
117 12
|
6天前
|
Dubbo Cloud Native 应用服务中间件
阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。
在云原生时代,微服务架构成为主流。阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。示例代码展示了如何在项目中实现两者的整合,通过 Nacos 动态调整服务状态和配置,适应多变的业务需求。
16 2
|
2月前
|
JSON SpringCloudAlibaba Java
Springcloud Alibaba + jdk17+nacos 项目实践
本文基于 `Springcloud Alibaba + JDK17 + Nacos2.x` 介绍了一个微服务项目的搭建过程,包括项目依赖、配置文件、开发实践中的新特性(如文本块、NPE增强、模式匹配)以及常见的问题和解决方案。通过本文,读者可以了解如何高效地搭建和开发微服务项目,并解决一些常见的开发难题。项目代码已上传至 Gitee,欢迎交流学习。
149 1
Springcloud Alibaba + jdk17+nacos 项目实践
|
2月前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源
|
2月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
|
2月前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
1月前
|
数据管理 Nacos 开发者
"Nacos架构深度解析:一篇文章带你掌握业务层四大核心功能,服务注册、配置管理、元数据与健康检查一网打尽!"
【10月更文挑战第23天】Nacos 是一个用于服务注册发现和配置管理的平台,支持动态服务发现、配置管理、元数据管理和健康检查。其业务层包括服务注册与发现、配置管理、元数据管理和健康检查四大核心功能。通过示例代码展示了如何在业务层中使用Nacos,帮助开发者构建高可用、动态扩展的微服务生态系统。
77 0
|
22天前
|
负载均衡 应用服务中间件 Nacos
Nacos配置中心
Nacos配置中心
52 1
Nacos配置中心
|
18天前
|
监控 Java 测试技术
Nacos 配置中心变更利器:自定义标签灰度
本文是对 MSE Nacos 应用自定义标签灰度的功能介绍,欢迎大家升级版本进行试用。