Zookeeper
最后更新于
这有帮助吗?
最后更新于
这有帮助吗?
Zookeeper 是开源的分布式协调服务,它提供了一些简单的原语。应用程序可以基于这些原语实现一些复杂的高级服务。
不同于Redis的KV结构,zk的数据结构是一个类似目录树的结构:
层级结构中的每个节点称为 znode
,每个节点都可以存储数据,但是最大数据存储大小为1MB
。
他们统一存储在内存中(高性能、低延迟、高可用)。
存储的内容同Redis相同,也是二进制安全的,也就是说zk不关心他们的数据编码格式,给什么byte流就存储什么byte流。
为了保证zk快速、高可用的特点,不可以将zk当作数据库使用,这也是为什么每个znode只能存储1MB大小的原因。
每个Client在连接zk集群时,都会产生一个Session,代表当前的会话,依托于Session,可以将znode分为一下几种类型:
持久节点
临时节点,会话级别,会话不存在数据就会消失。Session消失通过事件通知删除节点,典型的应用场景为分布式锁(可以有效的避免死锁的问题,因为应用程序一旦结束会话也会结束,锁也会被释放)
序列节点
Zookeeper 通常也作为一个集群使用:
zk集群是一种主从复制集群(Redis Cluster是一种数据分片集群),主从复制集群中的每个Server的数据都是完全相同的,且主节点用作写操作,从节点用来扩展读操作
在集群中分为两种角色:Leader
和Follower
,Leader
写、Follower
读
当Client对Follower
发生写操作,会将写操作重定向给Leader
Zookeeper集群还是一个可以快速自我修复的集群环境:
主从复制集群的主要问题是Leader
节点的单点故障问题
当zk的Leader发生故障后,zk集群会进入无主模型,zk会拒绝所有的请求,此时集群处于不可用状态
此时zk内部多个Follower
会发生选举,重新选举出一个Leader(这个过程只有不到200ms,并可以立即处理请求,并恢复原来的吞吐量)
选举成功后,zk变为可用状态,并开始处理客户端请求
Zookeeper集群的读写性能也是极高的(横坐标为客户端读写的占比,纵坐标为每秒请求数):
Zookeeper同时具有以下特性:
顺序一致性:因为时主从模型,写操作只有一个Leader节点
原子性:操作一个znode,集群中的所有Server要么都操作成功,要不都不成功(强一致性写入会导致不可用性,故采用最终一致性,也就是过半写入)
统一视图:主从复制模型,连任何一个集群节点都可以看到所有的完整的数据(包括session,也就是说是session共享的)
持久性:zk的数据存储在内存中,同redis相同他也有持久化策略
实时性:在时间范围内是最新的数据
相比较于Redis,zk实现分布式锁:
zk具有统一视图,其一致性相比于Redis要好得多
锁的释放与死锁的判断可以通过基于session的临时节点,相比与Redis的方式(主动轮询)
基本没有什么延迟
如果等待锁的客户端较多,每次心跳将会给redis带来很大的压力
zk锁在释放时会回调所有的客户端,将会造成太大的压力
可以使用序列节点+watch
是新公平锁,减少回调的客户端
注意,每个等待的客户端都去watch前一个锁(像一个链表)
分布式锁的实现:
使用create命令添加一个znode,其名称为分布式锁的唯一标志
当有一把锁存在时,其他进程使用create加锁会失败
为了保证不出现死锁的情况,Redis会加上过期时间处理,但是zk不需要设置国企时间,只要保证这个节点的类型是临时节点即可。当应用程序的逻辑完成会自动结束会话,这时会自动释放锁。
分布式公平锁(队列式的锁):
首先创建锁的根节点
每当有个进程需要加锁时,会在根节点下创建一个有序的临时节点 create -e -s
进程加锁完毕后,会使用ls
命令查询刚刚创建的有序节点是否在子节点中序列最小,如果最小,说明获取到了锁
配置中心:
将配置数据放置在znode节点中,使用目录结构对配置分类,znode的值存储配置内容(注意最多只有1MB大小)
配置使用方watch目标配置znode,当配置发生变更时,通过事件NodeDataChanged
可以保证客户端可以实时更新配置内容
注册中心:
所有的应用程序启动会将其注册到一个znode的子节点下
所有的应用程序watch该znode,当znode的children发生变更时,会触发NodeChildrenChanged
事件,来通知注册中心的其他程序该服务的上线、下线的情况
一台zookeeper一般无法满足需求,通常都会对zookeeper集群
参考:https://blog.csdn.net/fu123123fu/article/details/81193780
Leader:
事务请求的唯一调度和处理者,保证集群事务处理的顺序性
集群内部各服务器的调度者
Follower:
处理客户端非事务请求、转发事务请求给Leader
参与事务请求Proposal 的投票(Leader 发起的提案,要求 Follower 投票,需要半数以上follower节点通过,leader才会commit数据)
参与Leader的投票选举
Observer:
观察者,不参与任何投票
其余部分与Follower一致
用于保证高可用,避免集群节点多,投票延时大的问题
增加observer不影响集群中事务处理能力,同时还能提高集群非事务处理能力
一个zookeeper集群要对外提供服务,必须要保证过半的机器正常工作并且彼此之间能够正常通信。
如果要搭建一个能够允许F台机器down掉的集群,那么就要部署2F+1
台服务器构成的 zookeeper 集群。因此3台机器构成的 zookeeper 集群,能够在挂掉1台机器后依然正常工作。一个5台机器集群的服务,能够对2台机器挂掉的情况下进行容灾。如果一个由6台服务器构成的集群,同样只能挂掉2台机器。因此,5台和6台在容灾能力上并没有明显优势,反而增加了网络通信负担。系统启动时,集群中的server会选举出一台server为 Leader,其它的就作为 follower(这里不考虑 observer 角色)。
Zookeeper集群具有以下特性:
扩展性:主要通过三个方面体现
集群角色分为 Leader、Follower、Observer,增加Follower和Observer可以扩展读能力,增加Observer在扩展读能力的情况下,不会影响zk的选举效率
采用读写分离的方式,且是一个主从复制集群,容易进行横向扩展
扩展方式通过修改配置文件即可
可靠性:攘其外必先安其内
攘其外:具有最终一致性
安其内:当zk集群出现问题时,可以快速进行自我修复,在修复过程中集群会变为不可用状态,恢复之后再提供服务
zk基于分布式算法ZAB,参见文章分布式一致性算法
。
watch机制是zk的核心。
客户端1通过watch命令监控某个节点
客户端2对节点进行一些操作,当发生操作后,客户端1会接受到一个事件通知
事件分为如下几个类型:
NodeCreated
NodeDeleted
NodeDataChanged
NodeChildrenChanged
None,客户端与Zookeeper断开连接