赞
踩
客户端
znode 可能含有数据,也可能没有。如果 znode 包含数据,那么数据存储为字节数组(byte array)。字节数组的具体格式特定于每个应用的实现,ZooKeeper 不直接 提供解析支持。不过一般情况下,以 UTF-8 或 ASCII 编码的字符串就已经够用了。
API 概述
ZooKeeper 的 API 可以抽象成以下方法:
create /path data
创建一个名为 /path 的 znode 节点,并包含数据 data
delete /path
删除名为 /path 的 znode
exists /path
检查是否存在名为 /path 的节点
setData /path data
设置名为 /path 节点数据为 data
getData /path
返回名为 /path 节点的所有子节点列表
需要注意的是,ZooKepper 并不允许局部写入或读取 znode 节点的数据。当设置一个 znode 节点的数据或读取时,znode 节点的内容会被整个替换或者全部读取。
zkCli.sh
ZooKeeper 安装包提供了客户端工具 zkCli.sh(zkCli.cmd)方便我们学习实验 API 接口,该工具位于 bin 目录下,启动命令格式为:
bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
后面一段“127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183”是 ZooKeeper 集群的 IP 和端口组成的字符串,客户端会以随机顺序连接到服务器中。连接上集群后,如下图显示:
zkCli 连接成功
其中 0x15a0d2841340001 是本次 session 的 id。
这儿简单的演示临时节点和监视点特性:
同时开两个 zkCli 分别为 A,B;
A 连接至 127.0.0.1:2181
A连接到 ZooKeeper 集群
B 连接至 127.0.0.1:2182。
B连接到 ZooKeeper 集群
A 创建临时节点:create -e /temp_a "temp a znode"
A 创建节点
B 查看节点数据并设置监控点:get /temp_a true
B 查看节点
A 关闭模拟客户端崩溃。
A 创建的临时节点被删除
A 关闭连接
因为监视点的设置,B 收到 /temp_a 删除通知
B 收到删除通知
开源客户端库
zkCli 只适合学习和试验 ZooKeeper 集群,如果我们需要利用 ZooKeeper 实际开发分布式协同任务的系统时,可以使用 ZooKeeper 自带的客户端库。不过这些 jar 包只提供基本的阻塞和非阻塞 API 接口,需要开发人员自己实现类似会话超时重连,重复设置监视点等功能。除了 ZooKeeper 自带的客户端包,还可以使用以下客户端模块。
zkClient
zkClient 是 Github 上一个开源的 ZooKeeper 客户端,是由 Datameer的工程师 Stefan Groschupf 和 Peter Voss 一起开发的。zkClient 在原生 API 上进行封装,是一个更简单易用的 ZooKeeper 客户端。同时,zkClient 在内部实现了诸如 session 超时重练,Watcher 反复注册等功能,使这些繁琐复杂的功能对开发人员透明。
Curator
Curator 是 Netflix 公司开源的一套 ZooKeeper 客户端框架,和 zkClient 一样,Curator 解决了很多 ZooKeeper 客户端非常底层的细节开发工作,目前已成为 Apache 的顶级项目,是全世界范围内使用最广泛的 ZooKeeper 客户端之一。
除了封装底层细节,使之对开发透明,Curator 还提供了一套易用性和可读性更强的 Fluent 风格的 API 框架。
除此之外,Curator 还提供饿了 ZooKeeper 各种应用场景的抽象封装(Recipe),如共享锁服务,Master 选举机制和分布式计数器等。
典型应用
需要再次说明 ZooKeeper 是保证分布式数据一致性和任务协调的框架,它并不会直接实现具体的分布式锁,Master 选举等分布式功能,而是提供一些简单易用的 API 接口,具体的功能实现需要开发者根据情况自行实现。另一方面,ZooKeeper 同时也是一个典型的发布/订阅模式的分布式数据管理与协调方案,配合监视点事件通知机制,可以非常方便地构建一些列分布式应用中都会涉及的核心功能:
数据发布/订阅
负载均衡
命名服务
分布式协调/通知
集群管理
Master 选举
分布式锁
分布式队列
下面简单介绍下分布式锁的实现思路。
分布式锁
假设一个应用由 n 个进程组成,分别为 p1, p2 .... pn,这些进程尝试获取一个锁从而进入临界区进行操作。这儿我们可以使用 ZooKeeper 的 znode 来代表一个锁,如“/lock”。
所有进程同时尝试创建这个 znode,由 ZooKeeper 来保证顺序性。
如果某进程成功创建了 “/lock”,假设为 p4,则代表 p4 抢占到了该分布式锁,p4 可以执行临界区代码,其他进程则会因为 znode 存在而创建失败。
它们可以在 “/lock”节点上创建监视器后等待 znode 删除通知,如果删除通知到来,则重复 1 抢占锁。
p4 释放锁时,删除 “/lock”节点即可,需要注意,因为 p4 很可能执行临界区代码时崩溃,所以“/lock”节点应该临时节点,如果 p4 崩溃,则该节点自行删除。
羊群效应
上述分布式锁的实现简单易用,但是却会造成“羊群效应”。想象一下,如果有 1000 个客户端同时调用 exists 监视“/lock”节点变化。那么当这个 znode 创建或删除时就会发送 1000 个通知,这个被监视的 znode 的一个变化会产生一个尖峰的通知,该尖峰时刻提交的操作可能会产生很高的延迟。
为了防止羊群效应的产生,利用有序节点,分布式锁不妨换一种实现方式:
系统已经存在“/lock”节点,争抢锁的进程在该节点下创建临时有序子节点,形如:/lock/192.168.1.200:2222-00000001;
进程获取 /lock 下子节点列表,判断自己的节点位置:
如果自己的节点序号最小,则表示获得锁,执行临界区代码;
如果不是,则在自己序号前一个子节点增加监视点 Watcher,等待删除通知。
获取锁的进程,执行外临界代码,删除自己的子节点,表示释放锁;
下一个子节点序号的进程获取删除通知,重复动作 2。
这种实现方式下,进程只监测排在它前面进程的节点,而不是 所有进程都监测 /lock 节点,避免了“羊群效应”。结构如下图所示:
分布式锁
ZooKeeper 和 Dubbo
Dubbo 是阿里巴巴开源(好像开源版本已经停止更新)的由 Java 语言编写的分布式服务框架,致力于提供高性能和透明化的远程服务调用方案和 SOA 服务治理方案。
Dubbo 基于 ZooKeeper 实现服务注册中心。注册中心是 RPC 框架最核心的模块之一,用于服务的注册和订阅。在 Dubbo 的实现中,对注册中心模块进行抽象封装,因此可以基于其提供的外部接口实现各种不同类型的注册中心,例如数据库,Redis 等。
在 Dubbo 注册中心的整体架构中,ZooKeeper 上服务的节点设计如下图所示:
分布式锁
/dubbo 这是 Dubbo 在 ZooKeeper 上创建的根节点
/dubbo/com.foo.BarService 是服务节点,代表了 Dubbo 的一个服务
/dubbo/com.foo.BarService/providers 这是服务提供者的根节点,其子节点代表每一个服务真正提供者
/dubbo/com.foo.BarService/consumers 这是服务消费者的根节点,其子节点代表每一个服务的真正消费者
流程说明:
服务提供者(Provider)启动
向 /dubbo/com.foo.BarService/providers 目录下写入自己的URL地址。
服务消费者(Consumer)启动时
订阅 /dubbo/com.foo.BarService/providers 目录下的提供者URL地址。
并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的URL地址。
监控中心(Monitor)启动时
订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者URL地址。
需要注意的是,所有提供者和消费者在 ZooKeeper 上创建的节点都是临时节点,利用临时节点生命周期和会话绑定的特性,一旦机器发生故障导致服务提供者无法对外提供服务,该临时节点就会自动从 ZooKeeper 上删除。
内容来源
从 Paxos 到 ZooKeeper 分布式一致性原理与实践
ZooKeeper 分布式过程协同技术详解
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。