Nacos注册中心客户端容灾

目前Nacos客户端有一个FailoverReactor来进行容灾文件的管理,可以通过在指定磁盘文件里写入容灾数据来进行客户端使用数据的覆盖。FailoverReactor目前会拦截Nacos客户端查询接口调用,以getAllInstances接口为例,目前FailoverReactor的工作流程如下图:


这里主要涉及到两个组件:

  • FailoverReactor:容灾数据管理类,负责容灾开关和数据的加载和刷新;
  • ServiceInfoHoler:默认的服务数据管理类,持有一份内存服务数据缓存,负责处理Nacos服务端的推送的最新数据;

FailoverReactor和ServiceInfoHoler的交互机制如下:

这里和客户端容灾的相关逻辑主要是3个:

  1. FailoverReactor会定期读取磁盘容灾开关文件;
  2. 当容灾开关打开时,FailoverReactor会从磁盘里加载容灾数据文件,同时用户调用查询请求时就会优先使用容灾文件里的数据;
  3. FailoverReactor会定期从ServiceInfoHolder拿到最新的内存数据,保存到容灾磁盘数据文件里;

目前的方式有四个问题:

  1. 无法人工覆盖容灾数据,当前是定期将ServiceInfoHolder里的数据进行持久化作为容灾数据。即使手动修改了容灾数据磁盘文件内容,也会被覆盖为ServiceInfoHolder里的数据;
  2. 基于磁盘的容灾缓存有一些限制,比如服务的订阅者有多个机器实例时,如果需要打开容灾开关,需要运维批量修改机器的文件;
  3. subscribe接口不会使用FailoverReactor里的数据;
  4. 容灾数据的可观测性也需要优化。

实现方案

上面提到的问题1、3和4都比较好解决,下面会一一阐述。针对问题2,一般在生产环境中,我们会考虑使用中心化的数据存储来进行容灾数据的存储和管理。我们可以将FailoverReactor依赖的数据 来源抽象为一个SPI接口FailoverDataSource,这个接口默认实现还是本地磁盘,但是用户可以实现这个SPI接口来使用自定义的容灾数据源。

接口和数据结构定义

FailoverDataSource的定义为:

public interface FailoverDataSource {

     FailoverSwitch getSwitch();

     Map<String, FailoverData> getFailoverData();
}

各个方法的作用为:

  • FailoverSwitch getSwitch():获取当前的容灾开关;
  • Map<String, FailoverData> getFailoverData():获取当前的容灾数据,返回一个map,key是服务名,value为对应服务的容灾数据;

FailoverSwitch的定义建议如下:

public class FailoverSwitch {

    private boolean enabled;
}
  • enabled:容灾是否打开;

FailoverData的定义建议为:

public abstract class FailoverData {

    private DataType dataType;
    
    private Object data;
    
    protected FailoverData(Object data, DataType dataType) {
        this.data = data;
        this.dataType = dataType;
    }

    enum DataType {
        naming,
        config
    }
}
  • dataType:容灾数据类型,这里因为需要综合考虑配置模块的容灾,所以使用抽象类定义了naming和config两种容灾数据类型;
  • data:容灾数据,子类设置具体类型;

以naming模块为例,NamingFailoverData扩展FailoverData:

public class NamingFailoverData extends FailoverData {

    private NamingFailoverData(ServiceInfo serviceInfo) {
        super(serviceInfo, DataType.naming);
    }

    public static NamingFailoverData newNamingFailoverData(ServiceInfo serviceInfo) {
        return new NamingFailoverData(serviceInfo);
    }
}

NamingFailoverData里的容灾数据类型为ServiceInfo。

交互流程

FailoverReactor内部流程

FailoverReactor和FailoverDataSource、ServiceInfoHoler的交互机制优化为:

各个组件职责说明如下:

  • FailoverReactor:存储容灾缓存数据,管理容灾开关定时任务和容灾数据更新定时任务;
  • FailoverSwitchRefresher:定时(每5秒)从容灾数据源查询容灾开关,根据容灾开关的值进行相应操作:
    • 若容灾打开:从容灾数据源查询容灾数据FailoverDataSource,保存到FailoverReactor的内存容灾数据map里;
    • 若容灾关闭:清空FailoverReactor的内存容灾数据map;
  • FailoverDataSource:存储容灾数据,前文已经提及;
  • ServiceInfoHoler:默认情况下服务数据的管理,前文已经提及;

客户端查询请求流程

对于客户端的查询请求,其流程优化为:

这里的流程和之前的变化为:当从failoverReactor里拿不到容灾数据的时候,还是会去serviceInfoHolder里读取数据。这么做的目的是因为我们可能只配置部分服务进行容灾,其他的服务还是走serviceInfoHolder。

订阅接口事件通知流程

对于订阅接口,之前是不会受到容灾开关的影响,现在则也会在容灾开启时停止数据更新通知:

public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {
        ...
        if (changed) {
            NAMING_LOGGER.info("current ips:({}) service: {} -> {}", serviceInfo.ipCount(), serviceInfo.getKey(),
                    JacksonUtils.toJson(serviceInfo.getHosts()));
            // 判断容灾开关是否打开,打开时不发布事件:
            if (!failoverDataSource.getFailoverSwitch().isEnabled()) {
                NotifyCenter.publishEvent(new InstancesChangeEvent(notifierEventScope, serviceInfo.getName(), serviceInfo.getGroupName(),
                        serviceInfo.getClusters(), serviceInfo.getHosts()));
            }
            DiskCache.write(serviceInfo, cacheDir);
        }
        return serviceInfo;
    }

同时,在容灾关闭时,我们需要根据容灾期间数据是否发生变化来决定要不要触发订阅事件通知,我们可以把这个逻辑驾到上面提到的FailoverSwitchRefresher里:

在这里当FailoverSwitchRefresher轮询发现容灾关闭时,在清空FailoverReactor的内存数据之前,会触发FailoverReactor和ServiceInfoHolder的数据比较,如果发现数据不一致,则会触发ServiceInfoHolder发布对应的服务变更事件。

可观测性

我们可以定义个MultiGauge来存储FailoverReactor里目前生效的容灾数据内容,统计粒度为每个服务当前有多少个实例:

MultiGauge failoverInstanceCounts = MultiGauge.builder("nacos_naming_client_failover_instances").register(Metrics.globalRegistry);

Set<String> serviceNames = failoverDataSource.getSwitch().getServiceNames();
Map<String, FailoverData> failoverDataMap = failoverDataSource.getFailoverData();

failoverInstanceCounts.register(serviceNames.stream().map(serviceName -> MultiGauge.Row.of(Tags.of("service_name", serviceName), ((ServiceInfo)failoverDataMap.get(serviceName)).ipCount())).collect(Collectors.toList()), true);

测试用例

以下场景需要进行测试:

  • 打开容灾开关后,对于包含在容灾的服务列表里的服务,Nacos服务端数据变化不影响客户端查询和订阅;
  • 关闭容灾开关后,对于所有服务客户端查询到最新数据;
  • 关闭容灾开关后,若订阅的服务数据在容灾期间有变化,会触发一次订阅通知;
  • 设置容灾的服务列表,不在服务列表里的不会使用容灾数据;
  • 使用自定义的容灾实现,可以被加载并运行;

原文出自:Nacos官网 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/230550.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】浅谈信号量

文章目录 一、共享内存的弊端新概念引入 二、理解信号量原子性 tips&#xff1a;system V 是一套标准&#xff0c;共享内存&#xff0c;信号量&#xff0c;消息队列属于system V。 一、共享内存的弊端 进程A和进程B进行通信时&#xff0c;假如进程A向物理内存的共享区写入&quo…

深入浅出:HTTPS单向与双向认证及证书解析20231208

介绍: 网络安全的核心之一是了解和实施HTTPS认证。本文将探讨HTTPS单向认证和双向认证的区别&#xff0c;以及SSL证书和CA证书在这些过程中的作用&#xff0c;并通过Nginx配置实例具体说明。 第一部分&#xff1a;HTTPS单向认证 定义及工作原理&#xff1a;HTTPS单向认证是一…

前端:HTML+CSS+JavaScript实现轮播图2

前端&#xff1a;HTMLCSSJavaScript实现轮播图2 1. 和之前版本的区别2. 实现原理3. 针对上述的改进3. 参考代码 1. 和之前版本的区别 之前发布的那篇关于轮播图的文章在这&#xff1a;前端&#xff1a;HTMLCSSJavaScript实现轮播图&#xff0c;只能说存在问题吧&#xff01;比…

Redis KEY*模糊查询导致速度慢、阻塞其他 Redis 操作

Redis KEY*模糊查询导致交互速度慢、阻塞其他 Redis 操作 查询速度慢的原因 在Redis中&#xff0c;使用通配符 KEYS 命令进行键的模糊匹配&#xff08;比如 KEYS key*&#xff09;可能会导致性能问题&#xff0c;尤其是在数据集较大时。这是因为 KEYS 命令的实现需要遍历所有…

输入/输出控制详解(块、字符设备?程序控制?中断、DMA又是啥?)

输入/输出&#xff08;I/O&#xff09; 输入/输出&#xff08;I/O&#xff09;控制是计算机系统中的一个关键概念&#xff0c;涉及到计算机与外部设备之间的数据传输。计算机系统通过输入设备接收用户输入&#xff0c;通过输出设备向用户显示结果。输入/输出控制包括管理和协调…

1.查看表的基本结构,表的详细结构和修改表名

查看表的基本结构,表的详细结构和修改表名 1.查看数据表基本结构 有强迫症或健忘症的小伙伴们在建好数据库和表以后&#xff0c;通常会怀疑自己刚才是不是敲错了&#xff0c;怎么办&#xff1f;如果不是使用图形界面是不是就没法查看啦&#xff1f; 不存在的&#xff0c;这就…

PDF控件Spire.PDF for .NET【转换】演示:将 SVG 转换为 PDF

SVG 是一种矢量图形文件格式&#xff0c;用于创建可缩放且不损失质量的图像。然而&#xff0c;PDF由于支持高质量打印、加密、数字签名等功能&#xff0c;更适合共享和打印。将SVG转换为PDF可以保证图像在不同设备和环境下都有良好的显示效果&#xff0c;并更好地保护知识产权。…

【数据结构 — 排序 — 插入排序】

数据结构 — 排序 — 插入排序 一.排序1.1.排序的概念及其运用1.1.1排序的概念1.1.2排序运用1.1.3 常见的排序算法 二.插入排序2.1.直接插入排序2.1.1.算法讲解2.1.2.代码实现2.1.2.1.函数定义2.1.2.2.算法接口实现2.1.2.3.测试代码实现2.1.2.4.测试展示 2.2.希尔排序2.2.1.算法…

苹果IOS在Safari浏览器中将网页添加到主屏幕做伪Web App,自定义图标,启动动画,自定义名称,全屏应用pwa

在ios中我们可以使用Safari浏览自带的将网页添加到主屏幕上&#xff0c;让我们的web页面看起来像一个本地应用程序一样&#xff0c;通过桌面APP图标一打开&#xff0c;直接全屏展示&#xff0c;就像在APP中效果一样&#xff0c;完全体会不到你是在浏览器中。 1.网站添加样式 在…

Leetcode2477. 到达首都的最少油耗

Every day a Leetcode 题目来源&#xff1a;2477. 到达首都的最少油耗 解法1&#xff1a;贪心 深度优先搜索 题目等价于给出了一棵以节点 0 为根结点的树&#xff0c;并且初始树上的每一个节点上都有一个人&#xff0c;现在所有人都需要通过「车子」向结点 0 移动。 对于…

基于ResNet模型的908种超大规模中草药图像识别系统

中草药药材图像识别相关的实践在前文中已有对应的实践了&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《python基于轻量级GhostNet模型开发构建23种常见中草药图像识别系统》 《基于轻量级MnasNet模型开发构建40种常见中草药图像识别系统》 在上一篇文章中&…

操作系统 2-6 课后作业2.3:系统调用

第1关版本1内核执行的完整系统调用序列 任务描述 分析版本1内核&#xff0c;回答下列问题&#xff1a; 从系统开机直到输出第 4 个字符‘1’&#xff0c;系统依次执行了哪些系统调用&#xff1f;分别是在几号进程中执行的&#xff1f;&#xff08;对于一组连续出现的 0 号进程…

进程控制与原语

一、 进程的五种状态 在操作系统中&#xff0c;一个进程可以经历五种基本状态&#xff0c;这被称为进程的五种基本状态模型。这包括&#xff1a; 创建状态&#xff08;Create/New&#xff09;&#xff1a; 进程刚刚被创建&#xff0c;但还未被执行。在这个状态下&#xff0c;操…

JAVA 锁

乐观锁 乐观锁是一种乐观思想&#xff0c;即认为读多写少&#xff0c;遇到并发写的可能性低&#xff0c;每次去拿数据的时候都认为别人不会修改&#xff0c;所以不会上锁&#xff0c;但是在更新的时候会判断一下在此期间别人有没有去更新这个数据&#xff0c;采取在写时先读出…

CGAL的多边形曲面重建

1、介绍 该软件包实现了一种基于假设和选择的方法&#xff0c;用于从点云重建分段平面对象。该方法将从分段平面对象采样的无序点集作为输入。输出是对输入点集进行插值的紧凑且不透水的曲面网格。该方法假设提供了所有必要的主平面&#xff08;或者可以使用在形状检测中描述的…

如何使用玻璃材质制作3D钻石模型

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 当谈到游戏角色的3D模型风格时&#xff0c;有几种不同的风格&#xf…

MySQL 8 update语句更新数据表里边的数据

数据重新补充 这里使用alter table Bookbought.bookuser add userage INT after userphone;为用户表bookuser在userphone列后边添加一个类型为INT的新列userage。 使用alter table Bookbought.bookuser add sex varchar(6) after userage ;为用户表bookuser在userage 列后边添…

Hibernate 框架 (2023年架构师下半年案例分析题)

Hibernate 是一种对象和关系之间映射的框架&#xff0c;是 Java 应用和关系数据库之间的桥梁。它可以将数据库资源映射为一个或者多个 POJO。将面向数据库资源的各种业务操作以 POLO 的属性和方法的形式实现&#xff0c;使人们摆脱烦琐的 JDBC 代码&#xff0c;将精力更多地集中…

C/C++,优化算法——双离子推销员问题(Bitonic Travelling Salesman Problem)的计算方法与源代码

1 文本格式 // C program for the above approach #include <bits/stdc.h> using namespace std; // Size of the array a[] const int mxN 1005; // Structure to store the x and // y coordinates of a point struct Coordinates { double x, y; } a[mxN]; //…

【Linux下基本指令 —— 2】

Linux下基本指令 —— 2 十.more 指令语法&#xff1a;功能&#xff1a;常用选项&#xff1a;举例&#xff1a;Xshell7展示十一.less 指令语法&#xff1a;功能&#xff1a;选项&#xff1a;Xshell7展示 十二.head 指令语法&#xff1a;功能&#xff1a;选项&#xff1a;Xshell…