cassandra学习笔记五
Cassandra集群没有中心节点,各个节点的地位完全相同,它们通过一种叫做gossip的协议维护集群的状态。通过gossip,每个节点都能知道集群中包含哪些节点,以及这些节点的状态,这使得Cassandra集群中的任何一个节点都可以完成任意key的路由,任意一个节点不可用都不会造成灾难性的后果。
一、Gossip算法背景
Gossip算法如其名,灵感来自办公室八卦,只要一个人八卦一下,在有限的时间内所有的人都会知道该八卦的信息,这种方式也与病毒传播类似,因此Gossip有众多的别名“闲话算法”、“疫情传播算法”、“病毒感染算法”、“谣言传播算法”。但Gossip并不是一个新东西,之前的泛洪查找、路由算法都归属于这个范畴,不同的是Gossip给这类算法提供了明确的语义、具体实施方法及收敛性证明。
二、Gossip算法特点
Gossip算法又被称为反熵(Anti-Entropy),熵是物理学上的一个概念,代表杂乱无章,而反熵就是在杂乱无章中寻求一致,这充分说明了Gossip的特点:在一个有界网络中,每个节点都随机地与其他节点通信,经过一番杂乱无章的通信,最终所有节点的状态都会达成一致(很神奇)。每个节点可能知道所有其他节点,也可能仅知道几个邻居节点,只要这些节点可以通过网络连通,最终他们的状态都是一致的。
三、Gossip本质
Gossip是一个带冗余的容错算法,更进一步,Gossip是一个最终一致性算法。虽然无法保证在某个时刻所有节点状态一致,但可以保证在“最终”所有节点一致,“最终”是一个现实中存在,但理论上无法证明的时间点。因此Gossip适合没有很高一致性要求的场景。
因为Gossip不要求节点知道所有其他节点,因此又具有去中心化的特点,节点之间完全对等,不需要任何的中心节点。实际上Gossip可以用于众多能接受“最终一致性”的领域:失败检测、路由同步、Pub/Sub、动态负载均衡。
但Gossip的缺点也很明显,冗余通信会对网路带宽、CUP资源造成很大的负载,而这些负载又受限于通信频率,该频率又影响着算法收敛的速度。
四、Gossip节点的通信方式及收敛性
Gossip中的每个节点维护一组状态,状态可以用一个key/value对表示,还附带一个版本号,版本号大的状态比版本号小的新。两个节点(A、B)之间存在以下三种通信方式:
通信方式 |
含义 |
push |
A节点将数据(key,value,version)及对应的版本号推送给B节点,B节点更新A发过来的数据中比自己新的数据 |
pull |
A不发送数据的value,仅发送数据的摘要key和version给B。B根据版本比较数据,将本地比A新的数据(key,value,version)推送给A,A更新自己的本地数据 |
push/pull |
与pull类似,A仅发送摘要给B。不同之处在于,B比较版本后,不仅将比A新的数据发送给A,同时还向A请求A的摘要中比自己新的数据 |
如果把两个节点数据同步一次定义为一个周期,则在一个周期内,push需通信1次,pull需2次,push/pull则需3次,从效果上来讲,push/pull最好,理论上一个周期内可以使两个节点完全一致。直观上也感觉,push/pull的收敛速度是最快的。
假设每个节点通信周期都能选择(感染)一个新节点,则Gossip算法退化为一个二分查找过程,每个周期构成一个平衡二叉树,收敛速度为O(n2),对应的时间开销则为O(logn)。这也是Gossip理论上最优的收敛速度。但在实际情况中最优收敛速度是很难达到的。
显然pull的收敛速度大于push,而每个节点在每个周期被感染的概率都是固定的p(0<p<1),因此Gossip算法是基于p的平方收敛,也成为概率收敛,这在众多的一致性算法中是非常独特的。
Gossip的节点的工作方式又分为以下两种:
Anti-Entropy(反熵):以固定的概率传播所有的数据
Rumor-Mongering(谣言传播):仅传播新到达的数据
Anti-Entropy模式有完全的容错性,但有较大的网络、CPU负载;Rumor-Mongering模式有较小的网络、CPU负载,但必须为数据定义”最新“的边界,并且难以保证完全容错,对失败重启且超过”最新“期限的节点,无法保证最终一致性,或需要引入额外的机制处理不一致性。
五、cassandra中Gossip的节点同步规则
当一个节点启动时,获取配置文件中的seeds配置。cassandra作为一个去中心化的分布式系统,没有中心节点的存在。但为了让节点启动时能与集群通信,仍然需要为它配置最少一个seed节点。
Cassandra内部有一个Gossiper,每隔一秒运行一次(在Gossiper.java的start方法中),按照以下规则向其他节点发送同步消息:
1.随机取一个当前活着的节点,并向它发送同步请求
2.随机取一个不可达的节点,并向它们发送同步请求
3.如果第一步中所选择的节点不是seed,或者当前活着的节点数少于seed数,则向随意一台seed发送同步请求
第一步的目的是和目前活着的节点同步状态,第二步的目的是尽快发现已下线的节点重新上线了。第三步中的第一个条件,是因为seed理论上总是有较多的节点状态信息,若第一次同步的节点不是seed,则应该再和seed同步一下。第三步中的第二个条件则是为了避免出现seed孤岛。
如果没有这个判断,考虑这样一种场景,有4台机器,{A,B,C,D},并且配置了它们都是seed,如果它们同时启动,可能会出现这样的情形:
A节点起来,发现没有活着的节点,走到第三步,和任意一个种子同步,假设选择了B。B节点和A完成同步,则认为A活着,它将和A同步,由于A是种子,B将不再和其他种子同步。C节点起来,发现没有活着的节点,同样走到第三步,和任意一个种子同步,假设这次选择了D。C节点和D完成同步,认为D活着,则它将和D同步,由于D也是种子,所以C也不再和其他种子同步。
这时就形成了两个孤岛,A和B互相同步,C和D之间互相同步,但是{A,B}和{C,D}之间将不再互相同步,它们也就不知道对方的存在了。
加入第二个判断后,A和B同步完,发现只有一个节点活着,但是seed有4个,这时会再和任意一个seed通信,从而打破这个孤岛。
六、cassandra中Gossip的实现
Cassandra采用的通信方式是push/pull,如前文所述,push/pull有三个阶段。在每个阶段,节点之间都需要传递一些自己的状态信息。状态信息的传递是通过封装在一种特定的消息里传递,每个阶段传递的消息格式均不同,如下表:
消息名 |
含义 |
GossipDigitsSynMessage |
A向B请求同步 |
GossipDigitsAckMessage |
B返回自己拥有的比A新的数据给A |
GossipDigitsAck2Message |
A再返回自己拥有的比B新的数据给B |
Gossip互相之间通信,通过上表的消息封装需要传递的信息。节点间互相交换的状态信息主要有以下3种:
状态信息 |
含义 |
HeartBeat |
心跳信息,由generation和version组成。generation每次系统启动都加1,用于区分重启前后的状态 |
ApplicationState |
用于表示系统状态,存储系统的负载信息等 |
EndPointState |
维护节点自身数据的全局version,并封装HeartBeat和ApplicationState |
Cassandra的每个节点都实现了IEndPointStateChangeSubscriber接口的,它负责处理接收到的消息,该接口包含以下方法:
方法名 |
含义 |
onjoin |
有机器加入到集群中 |
onChange |
有状态发生变更了 |
onAlive |
机器可用 |
onDead |
机器不可用 |
这里假设192.168.1.1(源节点)决定和192.168.1.2(目标节点)同步,首先源节点向目标节点发送GossipDigestSynMessage包,这个包有本机维护的所有节点的状态信息的最新版本摘要,摘要只包含key和version,不包含具体的value,这样可以减小同步的带宽消耗。
当目标节点收到GossipDigestSynMessage包时,它需要做两件事:
1.找出收到的消息中比本地版本新的状态,按照版本号差异大小排序,将这些状态的摘要放入GossipDigestAckMessage中。
2.找出本地比源节点版本更新的状态,放入GossipDigestAckMessage中,并将它发送回源节点。
这里按照版本号差异大小排序的原因是每个Message允许发送的状态数量是有限的(参见Gossip.java中的MAX_GOSSIP_PACKET_SIZE定义),这样可以保证比较老的状态(版本号差异大的)可以优先得到更新。
源机器接收到GossipDigestAckMessage后,同样也做两件事:
1.使用目标节点发送过来的比自己新的状态更新本地的状态,源节点获取到了目标节点上比自己更新的状态。
2.源节点把包含在GossipDigestAckMessage中,目标节点向自己请求更新的摘要对应的状态信息通过GossipDigestAck2Message发送到目标服务器。
目标服务器更新本地的状态,这样目标服务器也获取到了源节点上比自己更新的状态。完成这样一次同步后,源节点和目标节点上的状态都得到了同步。
cassandra学习笔记五相关推荐
- python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用
本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...
- Ethernet/IP 学习笔记五
Ethernet/IP 学习笔记五 Accessing data within a device using a non-time critical message (an explicit mess ...
- StackExchange.Redis学习笔记(五) 发布和订阅
StackExchange.Redis学习笔记(五) 发布和订阅 原文:StackExchange.Redis学习笔记(五) 发布和订阅 Redis命令中的Pub/Sub Redis在 2.0之后的版 ...
- 吴恩达《机器学习》学习笔记五——逻辑回归
吴恩达<机器学习>学习笔记五--逻辑回归 一. 分类(classification) 1.定义 2.阈值 二. 逻辑(logistic)回归假设函数 1.假设的表达式 2.假设表达式的意义 ...
- 好程序员教程分析Vue学习笔记五
好程序员教程分析Vue学习笔记五,上次我们学习了Vue的组件,这次我们来学习一下路由的使用.在Vue中,所谓的路由其实跟其他的框架中的路由的概念差不多,即指跳转的路径. 注意:在Vue中,要使用路由, ...
- 【AngularJs学习笔记五】AngularJS从构建项目开始
为什么80%的码农都做不了架构师?>>> #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...
- ROS学习笔记五:理解ROS topics
ROS学习笔记五:理解ROS topics 本节主要介绍ROS topics并且使用rostopic和rqt_plot命令行工具. 例子展示 roscore 首先运行roscore系列服务,这是使用R ...
- Spring Boot 框架学习笔记(五)( SpringSecurity安全框架 )
Spring Boot 框架学习笔记(五) SpringSecurity安全框架 概述 作用 开发示例: 1. 新建项目 2. 引入依赖 3. 编写`SecurityConfig`类,实现认证,授权, ...
- Java学习笔记(五):一张图总结完JVM8基础概念
Java学习笔记(五):一张图总结完JVM8基础概念 引文 最近在学习JVM的相关内容,好不容易把基础概念全部都学了一遍,却发现知识网络是零零散散的.迫不得已,只好再来一次总的归纳总结.为了更好的理解 ...
最新文章
- gulp-sass_如果您是初学者,如何使用命令行设置Gulp-sass
- 计算机 二进制 中国,二进制与计算机
- linux环境搭建seafile客户端自动上传文件
- WinForm创建系统托盘以及操作注册表
- selenium python怎么读_selenium+Python中的面试总结
- ios开发笔记之 APNS推送服务的实现
- centos 设置双网卡,双网关
- async js 返回值_JS异步编程 | Async / Await / Generator 实现原理解析
- wxWidgets:线程间和进程间通信
- Python入门之数据类型
- 22504!Windows 11 新预览版发布
- 第八十二期:掌握这些监控报警优化技巧,百万年薪不在话下!
- IDEA上Debug调试全流程
- 鸿蒙系统发布会16号几点,华为鸿蒙手机系统正式定档发布,12月16日于我们见面...
- 诺基亚9 PureView五摄机皇再曝光 低配高价毫无诚意?
- C#限制float有两位小数
- 《深入理解Windows操作系统》笔记1
- CAD将图形输出成png图片的三种方法
- 项目管理java_java项目管理经验总结
- REVIT插件 | 建模助手这次的版本更新,BIMer都笑了