ZooKeeper
对强一致性的理解
顺序一致性 Order及时性读已提交 RC单系统快照(可重复读 RR) ZooKeeper 概述
ZooKeeper 的强一致性设计目标
简单:可靠:强一致性:高效: 节点数据模型瞬时节点顺序节点watches 监听器API架构集群配置使用场景
对强一致性的理解分布式的,高性能的协调服务
参考链接:MIT 6.824: Distributed Systems —— Lecture 8: Zookeeper
Linearizeability 可串行的(强一致性)要求
操作存在一个全局唯一的 Order
实时(Matches real time)
读已提交 RC,RR(reads see preceding write in the order)
顺序一致性 Order及时性只要最终 w 的值的变化存在全局唯一的顺序。则认为符合顺序一致性
读已提交 RC在一定的时间内及时同步完成,或者 zookeeper 可以通过调用 sync 强制同步
单系统快照(可重复读 RR)zookeeper 可以通过调用 sync 强制同步
服务端过滤重复的请求,这个 zookeeper 好像也通过 zxid 解决了,好像
ZooKeeper 概述有没有这样一个系统,帮助我们解决这些问题呢? 有!!!ZooKeeper
ZooKeeper 的强一致性协调服务很难做好。他们特别容易出错,比如竞争和死锁。
zookeeper :分布式应用的,服务协调器,提供同步(synchronization)、配置(configuration)以及组和服务发现(naming)功能
保证串行写入(写入强一致性),客户端收到更新成功前,数据已经同步到所有副本
保证客户端 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);
...
// 节点有自己的子节点
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 上设置永久的、递归的监视器
APIclass ZooKeeper {
public String create();
public void delete();
public Stat exists();
public byte[] getData();
public Stat setData();
public List 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 开抢),时序锁)
分布式队列(详见:百度)



