浅谈zookeeper

浅谈Zookeeper

CP 模型,zookeeper是一个分布式协调服务,所谓分布式协调主要是来解决分布式系统中多个进程之间的同步限制,防止出现脏读,例如我们常说的分布式锁。类似文件存储结构,不过每个节点都可存储数据,最多1MB。
在这里插入图片描述

注意:zk服务中,2888端口用于follower调用leader进行写操作,3888端口为选主使用端口,2181端口为客户端连接zk服务节点端口。

一、角色介绍

1.Leader:领导者

1)zk中心,负责协调集群中的其他节点
2)发起与提交写请求
3)与learner保持心跳
4)崩溃恢复时,负责恢复数据,以及同步数据到learner

2.Follower:跟随者

1)与leader保持心跳
2)当leader崩溃时,发起投票,选举新的leader
3)处理leader发送的消息与请求

3.Observer:观察者

写操作,需要半数Follower确认,所有节点越多,zk写性能越差,为了提高zk读性能,同时不影响写操作,增加的Observer。
1)与leader同步数据
2)数据加载到内存,不会持久化到硬盘
Learner = Follower + Observer,接受leader的数据同步
读写比例7:3或8:2,机器数量5或7台,成本低性能好

二、选主流程

由Follower节点选举完成,分为:1)启动时选主、2)失效后选主。投票信息包括,myid(服务器标识符)与zxid(事务id,完成数据同步的情况,数值越大,数据同步越完整)。
投票规则:优先投自己,再看zxid,相同比myid,半数即通过。

举例,

1)发起投票信息,三台服务器发起如下投票信息host1(myid:1,zxid:0)host2(myid:2,zxid:0)host3(myid:3,zxid:0)2)接收投票信息,检查是否为最新投票信息,并校验是否是looking状态的服务器发起的;
3)统计投票结果,zxid大优先,如果相同,myid大优先。host1接收到host2的投票信息,zxid相同,但是host2.myid > host1.myid。host1更改自己的投票信息host1(myid:2,zxid:0)。当前投票host2的总数为2 > 3/2,所以选择host2作为leader。

Leader失效后,暂停处理事务性的会话请求,所有follower状态更改为looking,然后重复上述操作。

  • 事务性:服务器状态改变,如节点的新建、删除、更新等
  • 非事务性:查询数据节点等

三、数据一致性

原子广播,保证最终一致性。客户端如果想要读取最终态的数据,那么可以使用sync命令,来获取最终数据。

Learner节点的写请求,都会转到leader,leader接收后发送给所有的Follower,并统计Follower写入成功的数量,超过半数,认为该写请求提交成功。通知所有的Follower commint该写请求,确保崩溃或者重启后,该写操作不会丢失。

例如创建节点,

1)首先由客户端发送创建节点的指令给到zk节点,假设follower1;
2)follower1节点发现是写操作命令,则将该指令转发到leader节点执行;
3)leader节点更新自己zxid信息,也就是事务id信息;
4)leader节点先将创建节点信息同步到log日志中,然后再follower1和follower2各自的队列中放入创建节点写日志的指令,当follower节点接收到指令以后,执行写日志操作,写入日志成功以后,告诉leader写入完成;leader会判断目前是否已经有过半的节点(包含自己)已经写入完成,如果完成,则先在自己的内存中创建节点,然后将在follower对应的节点中加入在内存中创建节点的指令,然后follower接收到指令以后进行内存操作,操作完成以后告诉leader写入完成,同样需要过半完成;
5)将创建结束的消息返回给调用的follower,然后返回给客户端,节点创建结束。
  • 原子广播协议,可以看成两部分,首先原子就代表这只有成功或者失败,没有中间状态;而广播就是并不意味着所有节点都完成相关操作才算完成,只要过半节点是成功的,那么本次操作就算成功完成了。
  • 最终一致性,在第四步中,leader会将所有指令按照顺序放入每个follower对应的队列中,每个follower按顺序去执行队列中的指令,达到一个最终一致性的结果。

四、脑裂及处理措施

例如Zk集群中有一个大脑leader,当出现故障时,产生两个leader,称作脑裂。由于相互失去了联系,都以为是对方出了故障,两个节点像"裂脑"一样,本能地争抢"共享资源"、争抢"应用服务"。就会发生严重后果:1)共享资源被瓜分、两边"服务"都起不来了;2)同时读写"共享存储",导致数据损坏。

1.为什么过半机制?

不考虑过半机制,如下架构,当机房1与2之间断开通信(心跳),会投票选举出如下两个leader。
在这里插入图片描述

过半,而非等于一半,即可防止上述情况发生。
过半还有一个好处,快,所以叫快速领导者选举算法。

2.为什么是奇数台?

过半即可,偶数一样能满足要求,为什么建议奇数台?
因为2n与2n-1的容忍度都是n-1,站在解决资源的角度来看,奇数台即可满足要求。

3.如何处理脑裂?

默认:过半机制,可以有效预防脑裂,舍弃了CAP中的A高可用。

但是,当leader假死(网络原因导致心跳超时etc.),仍然可能导致脑裂。
1)双线心跳
2)共享资源加锁
3)加权策略

五、常见使用场景

ZooKeeper 是一个高可用的分布式数据管理与系统协调框架。基于对 Paxos 算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得 ZooKeeper 解决很多分布式问题。

1.分布式锁

分布式锁主要得益于Zookeeper为我们保证了数据强一致性。两类:
1)保持独占:所有试图获取这个锁的客户端,最终只有一个可以成功获取。做法:把zk上的一个Znode看作一把锁,通过create znode的方式来实现。所有客户端都去创建/distribute_lock节点,最终成功创建的那个客户端拥有这把锁。注册监听器来监听此znode,只要被删除,就通知其他客户端来加锁。
2)控制时序:所有试图获取该锁的客户端,都会被安排执行。父节点/distribute_lock已经存在,来一个请求,在其父节点下面创建一个临时节点,锁被试放后,按照序号获取锁。

i)zookeeper分布式锁与redis分布式锁

更推荐zookeeper分布式锁,Zookeeper速度慢,但是强数据一致性,加锁吞吐量低时使用;redis快,但数据一致性弱,加锁吞吐量高

  • redis加锁速度是zk的5-8倍左右(不同环境稍有差异),解锁速度是zk的2~4倍左右
  • 在锁竞争状态下,zk通过watch实现监听等待,在等待期间出让cpu资源;redis通过信号量实现等待通知,在等待期间也会出让cpu资源,zk和redis在锁竞争状态下差异不大
  • zookeeper获取锁时,需要经过命令从flower到leader,leader处理并同步给flower,leader判定同步结果并commit等多个步骤,且数据持久化实时进行,因此对cpu和磁盘的性能消耗都较大,且硬盘消耗更为突出
  • redis获取锁时,数据按槽重定向后直接执行,通过lua保持所操作的原子性,且主备同步采用异步方式,牺牲了数据一致性换取时效性,因此从数据上看,redis做分布式锁效率更高,对cpu消耗大,对硬盘基本无消耗
  • 防止死锁方面,zk通过session连接超时判定client失联从而删除锁节点防止死锁,具有一定的安全性;redisson通过设置数据超时间防止client失联后的死锁发生,也具有较好的安全性
  • 性能扩展性方面,redis可以通过扩展集群节点,分散处理,降低单节点压力,从而达到提升性能的目的;zk集群由于CP导向,追求数据一致性特点,增加集群节点仅对读取性能有提升,对于写入数据(分布式锁需要写入数据)无提升,因此提升手段目前仅限于(1)提升硬件性能;(2)修改落盘配置规则(带来数据一致性风险);(3)leader节点配置为不服务,仅处理写入操作。

2.分布式队列

分布式队列分为两类:
1)先进先出队列:与分布式控制时序锁原理一致。
2)队员聚集后按顺序执行:通常在/queue这个znode下面预先建立一个/queue/num节点,并赋值n(队列大小),之后每次有队列成员加入后,判断当前是否已经达到队列大小,决定此时是否执行。典型场景:分布式环境中,有一个大任务TaskA,需要在很多子任务完成(或条件就绪)情况下才可以进行。

3.负载均衡

指软负载均衡,分布式环境中,为了保证高可用,通常一个应用或者同一个服务的提供方会进行多份部署,消费者就基于软负载均衡,从中获取一台。

4.数据发布与订阅(配置中心)

发布与订阅模型,即所谓的配置中心,即发布者将数据发布到Znode,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。例如全局的配置信息、服务地址列表等。

配置信息,这类场景:应用在启动时,主动获取一次配置,同时在节点上注册一个Watcher。以后每次配置更新时,会实时通知订阅的客户端,从而达到获取最新配置的目的。

5.命名服务

分布式系统中,通过使用命名服务,客户端应用能够根据指定名字获取资源、服务地址、提供者信息等。被命名的实体通常为:集群中的机器、提供的服务地址、远程对象等。常见的就是分布式服务框架中的服务地址列表:通过zk提供的API创建节点,得到全局唯一的path,这个path就是一个名字。

例如Dubbo中使用ZK为其命名服务,维护全局服务地址列表:

1)生产者:服务提供者在启动时,向ZK上的指定节点/dubbo/${serviceName}/providers下写入自己的URL,完成服务发布。
2)消费者:在启动时,订阅providers目录下的提供者URL地址,向/dubbo/${serviceName} /consumers 目录下写入自己的 URL 地址。
3)注意:所有向ZK注册的地址都是临时节点,保证生产与消费者能自动感应资源变化。另外,Dubbo针对服务粒度的监控,订阅/dubbo/${serviceName} 目录下所有提供者和消费者的信息即可。

6.分布式日志收集系统

核心工作就是收集分布在不同机器的日志。收集器通常按照应用来分配收集任务单元,因此需要在ZK上创建一个以应用为path的节点P,将该应用的所有机器IP,以子节点的形式注册到节点P。这样一来当机器变动时,能够实时通知到收集器调度任务分配。最后,将信息存放到指定ZK节点上,即可动态获取。
前提是,数据量小,但是数据更新可能会比较快的情景。

7.分布式通知/协调

ZK中特有Watcher注册与异步通知机制,能够很好的实现分布式环境下不同系统之间的通知与协调,实现对数据变更的实时处理。有如下几种方式Watcher:
1)监听同一个Znode:不同系统对ZK上同一个Znode进行注册,监听Znode的变化(包括其子节点),当有系统对其update时,其他系统即可收到通知,并作出相应处理。
2)心跳检测机制检测系统与被检测系统之间不直接关联,通过ZK上的某个节点进行关联。大大减少系统耦合性。
3)系统调度模式:系统由控制台和推送系统两部分组成,控制台负责控制推送系统进行相应的推送工作,操作员对控制台的一些操作,实际是修改ZK某节点的状态,推送系统把这些变化通知给它们注册Watcher的客户端。
4)工作汇报模式:类似于任务分发系统,子任务启动后到ZK上注册一个临时节点,定期将自己的进度进行汇报(进度回写入此临时节点),任务管理者就能知道任务进度。


参考路径

  • https://blog.csdn.net/weixin_38612401/article/details/125216821
  • https://blog.csdn.net/weixin_41605937/article/details/122139706
  • https://blog.csdn.net/weixin_49539546/article/details/123762054
  • https://blog.csdn.net/weixin_39079222/article/details/72911976
  • https://blog.csdn.net/mll999888/article/details/125239097
  • https://baijiahao.baidu.com/s?id=1687337357484700521&wfr=spider&for=pc

22年9月记录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈年_H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值