ZooKeeper(zk)服务端与客户端的关系是通过TCP长连接建立的,这种连接保证了ZooKeeper服务端与客户端之间的通信和Watch事件的通知。
ZooKeeper是一个开源的分布式协调服务框架,它为分布式系统提供一致性服务。在ZooKeeper中,客户端通过TCP长连接连接到服务集群,这种连接从客户端第一次连接到服务端开始建立,并通过心跳检测机制来保持有效的会话状态。通过这个连接,客户端可以发送请求并接收响应,同时也可以接收到Watch事件的通知。这种连接机制确保了ZooKeeper服务端与客户端之间的紧密联系和高效的通信1。
此外,ZooKeeper的服务端与客户端之间的通信还涉及到一些关键概念,如会话(Session)和超时时间(SessionTimeOut)。会话是指客户端会话,是客户端连接服务端的一个TCP长连接。服务端的Watch事件通知也是通过该TCP连接进行的。会话从第一次连接开始就已经建立,之后通过心跳检测机制来保持有效的会话状态。超时时间的设置是为了在网络故障或客户端主动断开等情况下,只要在会话超时时间之内重新建立连接,则之前创建的会话依然有效。
综上所述,ZooKeeper服务端与客户端的关系是通过TCP长连接建立的,这种连接机制保证了服务端与客户端之间的高效通信和Watch事件的通知,是ZooKeeper实现其分布式协调服务功能的基础
ZAB协议(Atomic Broadcast 原子广播协议)
1 崩溃可恢复的
当服务框架启动或者Leader崩溃,网络中断或者重启,我们会选举新的Leader,当超过半数follower与Leader数据同步完成之后(保证数据状态一致)。此时恢复完成,退出恢复模式。
2 原子消息广播算法
核心原理(数据同步机制):leader将我们应用服务器的请求转换成一个事务Proposal,并且广播到集群中所有的Follower当中只要有超过半数(n/2+1)的Follower进行了正确的反馈之后,Leader会再次向所有的Follower分发Commit消息,要求其将前一个事务提交。
基于TCP协议,可以保证消息发送与接收的顺序性。
-
事务ID:ZXID,单调递增的唯一ID,在Leader广播事务之前会进行创建。
-
64位的数字
-
低32位,应用服务器每产生一次事务请求,就+1
-
高32位代表Leader的周期编号,每选举一个Leader,会从其中找出其本地日志中最大的ZXID,取高32位然后就+1;重新选举之后,低32位清零
-
-
保证了当前Leader不会以之前的Leader的ZXID,去提交与之前Leader ZXID 不同的事务请求的。如 Leader1提交了 ZXID1, 此时Leader1挂了,选举出Leader2,Leader2也不会使用ZXID1去提交新的事务
- 选举算法:Leader不可用,从其他Follower选举出 ZXID最大的机器作为Leader,他已经具有所有最新的事务提案。
Leader选举
选举算法中的zxid是从内存数据库中取的最新事务id,事务操作是分两阶段的(提出阶段和提交阶段)
- 提交阶段:leader生成提议并广播给followers , follower收到提议后先将事务**写到本地事务日志**,然后反馈ACK
- 提交阶段:leader收到半数以上的ACK后,再**广播commit消息**,同时将事务操作应用到内存中。
可见,选主只是选出了内存数据是最新的节点,仅仅靠这个是无法保证已经在leader服务器上提交的事务最终被所有服务器都提交。
比如leader发起提议P1,并收到半数以上follower关于P1的ACK后,在广播commit消息之前宕机了,选举产生的新leader之前是follower,未收到关于P1的commit消息,内存中是没有P1的数据。
而ZAB协议的设计是需要保证选出leader后,P1是需要应用到集群中的。这块的逻辑是通过选举后的数据同步来弥补。
zookeeper分布式锁
- 独占锁:
- 创建锁:在zk上**创建一个临时节点**/exclusive_lock/lock,会保证同时只有一个一台服务器能够创建成功
- 其他没有获取的服务器会就需要到/exclusive_lock上**注册一个子节点变更的Watcher(时间通知机制)**。
- 释放锁:
- 1 执行完正常业务之后
- 2 创建临时节点的服务器发生宕机
- 共享锁
- 创建锁:只能读不能写、写完只能在共享锁结束。 多个客户端请求共享锁都会创建一个**节点序号**
- 读请求:先来的序列号小,如果前面序列号都是读请求,那么同步执行读操作
- 如果前面序号有写请求,则需要等待
- 写请求:如果自己是最小的序列号,则执行
应用场景
数据发布与订阅
zk采用的时候推拉结合的方式:应用服务器想zk注册自己需要关注的节点,一旦节点数据发生变更,zk会主动的向应用服务器发送Watcher事件通知,应用服务器接受到这个消息之后,主动向zk拉取对应的最新的配置
Watcher事件通知:客户点可以在服务端创建一个Watcher事件监听,监听节点的变化:节点被创建,删除,数据更新,都会通知到客户端。
- 这里的客户端可以指比如:我们的开发服务端,zkCli.sh进行测试
- Watcher注册是一次性的,每次触发之后都需要重新进行注册
方法exists可以监听节点是否存在,然后做一项负载均衡的事情
配置可以是:机器列表,运行时开关配置,数据库配置信息等
负载均衡
心跳检测,自动移除节点
当服务器宕机,zookeeper因为没有检测到心跳,自动把该节点移除,并通知其他服务器,其他服务器得知该机器已宕机,在分配连接时,不会分配到这台机器上,这点也是标题说的在负载均衡中用到zookeeper的原因
命名服务
使用分布式全局唯一ID的分配机制,如 type-job-0000000003,可以来唯一标识一个API接口
分布式协调/通知
zk中特有的Watcher注册与异步通知机制,能够很好的实现分布式系统下的不同机器的,不同系统的协调与通知
- 实现对数据变更 的**实时处理**
- 实现的方式:**所有的应用服务器都会在zk上同一个数据节点注册一个监听器Watcher**,
如果数据发生变动之后,zk会主动告知所有监听的服务器有数据变更,他们就会主动拉取这种变更。
Zookeeper 集群节点为什么要部署成奇数
超过半数 quorum=(n/2+1)
1个:quorum = 1
2个:quorum = 2
如果有2个zookeeper,那么只要有1个死了zookeeper就不能用了,因为1没有过半,所以
2个zookeeper的死亡容忍度为0;一个就完全别想!!!避免单点问题哦。
所有说最少是3个节点适宜。
3->1;三个zookeeper,最多1个zookeeper可以不可用(2个超过3个的半数)。 4->1;四个zookeeper,最多1个zookeeper可以不可用(3个超过4个的半数)。 5->2;五个zookeeper,最多2个zookeeper可以不可用。 6->2;两个zookeeper,最多0个zookeeper可以不可用。
结论
会发现一个规律,2n和2n-1的容忍度是一样的,都是n-1,所以为了更加高效,何必增加那一个不必要的zookeeper呢。
zookeeper的选举策略也是需要半数以上的节点同意才能当选leader,如果是偶数节点可能导致票数相同的情况。
选举算法细节
重点:选举出zxid最大的节点即可,如果zxid相同的话,选举出sid 服务器id最大的节点。