ZooKeeper
文章目录
分布式的,高性能的协调服务
对强一致性的理解
参考链接:MIT 6.824: Distributed Systems —— Lecture 8: Zookeeper
Linearizeability 可串行的(强一致性)要求
-
操作存在一个全局唯一的 Order
-
实时(Matches real time)
-
读已提交 RC,RR(reads see preceding write in the order)
顺序一致性 Order
只要最终 w 的值的变化存在全局唯一的顺序。则认为符合顺序一致性
及时性
在一定的时间内及时同步完成,或者 zookeeper 可以通过调用 sync 强制同步
读已提交 RC
zookeeper 可以通过调用 sync 强制同步
单系统快照(可重复读 RR)
服务端过滤重复的请求,这个 zookeeper 好像也通过 zxid 解决了,好像
有没有这样一个系统,帮助我们解决这些问题呢? 有!!!
ZooKeeper
ZooKeeper 概述
协调服务很难做好。他们特别容易出错,比如竞争和死锁。
zookeeper :分布式应用的,服务协调器,提供同步(synchronization)、配置(configuration)以及组和服务发现(naming)功能
ZooKeeper 的强一致性
-
保证串行写入(写入强一致性),客户端收到更新成功前,数据已经同步到所有副本
-
保证客户端 FIFO 顺序(参考上文:顺序一致性)
设计目标
简单:
-
分层的命名空间(znodes,类似文件和目录)
-
数据保存在内存中,高吞吐量和低延迟
-
高性能、高可用,可用于大型分布式系统
-
严格的有序性意味着可以实现复杂的同步
-
可靠:
-
ZooKeeper 可以有自己的副本集
-
事务日志和快照都放在持久化存储中
-
客户端维护一个 TCP 连接,发送心跳、请求、获得响应、支持事件
强一致性:
- ZooKeeper 在每个操作都带一个递增的数字 zxid
高效:
- ZooKeeper 读写性能很快,尤其是读,适合当读写比 10:1 的时候
节点数据模型
- 像 linux 文件系统一样,允许即是文件也是目录
class DataNode {
// 每个节点有自己的数据
byte[] data;
// acl 访问控制列表,不继承父节点权限(用于限制谁可以做什么)
Long acl;
// 含版本号、访问控制列表变化、时间戳
public StatPersisted stat;
}
class DataTree {
DataTree(DigestCalculator digestCalculator) {
nodes.put("", root);
...
// 节点有自己的子节点
/** add the proc node and quota node */
root.addChild(procChildZookeeper);
nodes.put(procZookeeper, procDataNode);
procDataNode.addChild(quotaChildZookeeper);
nodes.put(quotaZookeeper, quotaDataNode);
addConfigNode();
}
}
class StatPersisted {
// 事务id,ZooKeeper 状态的每次更改都会收到一个 Zxid(所有变化的总排序)
// 为了避免时钟同步,zookeeper 不适用实时时间,可以将实时时间加在 znode 上
// 高32位是 epoch,标识 leader 关系;低32位用于递增计数
// leader 崩溃恢复后,follower 只听从当前 epoch 的 leader
// 创建 zxid
private long czxid;
// 修改 zxid
private long mzxid;
// 谁最后一次修改子节点
private long pzxid;
// znode 创建时间戳
private long ctime;
// 修改时间
private long mtime;
// 数据版本,对一个节点的每次更改都会增加一个版本号
private int dataVersion;
// 子节点版本
private int cversion;
// acl 版本号
private int aversion;
// 临时节点拥有者 sessionId
private int ephemeralOwner;
// 数据长度
private long dataLength;
// 子节点长度
private int numChildren;
}
-
ZooKeeper 被设计用于存储协调数据:(状态信息、配置、Naming)所以每个节点的数据通常很小(1~1000 byte)
- 如果想要存数据,可以存指针,地址,数据库 ID 号
-
每当 znode 的数据发生变化时,版本号就会增加
瞬时节点
-
当会话结束时,znode 被删除,删除有一定的延迟
-
临时节点不可以有子节点
顺序节点
-
通过 -s 指定创建顺序节点,顺序节点名称之后会带 10 位的自增数字,栗如
test0000000001
-
可以和临时节点结合起来用
watches 监听器
客户端收到更新成功前,数据已经同步到所有副本,所以结果通过 watches 来异步接收通知呗
-
客户端可以在 znode 上设置 watches (监听必须绑定在 zNode 上)
-
当 znode 变化时,watches将被触发并移除
-
连接中断,客户端将收到一个本地通知
-
3.6.0 中的新功能: 客户端还可以在一个 znode 上设置永久的、递归的监视器
API
class ZooKeeper {
public String create();
public void delete();
public Stat exists();
public byte[] getData();
public Stat setData();
public List<String> getChildren();
// Asynchronous sync. Flushes channel between process and leader
// 客户端想得到最新的数据,就发送一个 sync,将当前指令压栈等待同步后执行
// 异步的实现当前进程与 leader 之间的指定 path 的数据同步
public void sync();
}
架构
-
来自客户机的所有写请求都被转发到一个称为 leader 的服务器。其余的 ZooKeeper 服务器被称为 follower,接收来自 leader 的消息建议并传递消息
-
消息传递层负责在 leader 宕机后重新选举,并同步 follower 和 leader
-
ZooKeeper 使用自定义的原子消息传递协议(ZAB 协议)
-
ZooKeeper 的集群式通过 log 的方式同步???
集群配置
-
配置文件
initLimit = 连接到 leader 的时限 syncLimint = follower 与 leader 之间请求和应答时限
# server.x 与 data 目录中 myid 文件对应 # 第一个端口用于通讯,第二个端口用于选举 server.1=127.0.0.1:2991:3991 server.2=127.0.0.1:2992:3992 server.3=127.0.0.1:2993:3993
-
集群角色
-
leader
-
learner
-
follower:接手客户端请求、返回结果,可以参与选主过程投票
-
observer:观察者,不参与选主投票
-
-
-
更新模式
-
写请求 Forwarder 到 Leader,ZAB 协议广播,收到一半以上写成功的应答,持久化,报告客户端写成功了
-
WAL(Write-Ahead-Log),所有更新先写 WAL,然后对内存中数据更新,最后 ACK;(感觉 WAL 模式能用来做手动业务回滚啊)
-
定期 Snapshot
-
ZAB 协议:原子广播协议
-
选主模式
-
广播模式
-
-
使用场景
-
服务发现
-
配置中心
-
负载均衡(临时节点+slot 算法)
-
分布式通知与协调(通过zk 提供的 listener 机制实现串行,并行分布式通知。实现简单的调度(调度应用负责发布任务,工作应用负责执行))
-
集群服务监控(临时节点)
-
分布式锁(服务协调,独占锁(争创临时节点,抢不到 watch 开抢),时序锁)
-
分布式队列(详见:百度)