ZooKeeper 典型运用场景

基于ZAB 算法的实现,zk 可以很好的保证分布式环境中数据的一致性。

数据发布/订阅

发布者将数据发布到zk 的一个或者一系列节点上,供订阅者进行数据订阅。进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。

Zookeeper 采用推拉相结合的方式,客户端向服务端注册自己需要关注的节点,一旦该节点发生数据变更,那么服务端就会向相应的客户端发送watcher 事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。

如果将配置信息存放到zookeeper上进行集中管理,那么通常情况下,应用在启动的时候会主动到zk服务端进行配置信息的获取,同时,在指定节点上注册一个watcher 监听,这样一来,但凡配置信息发生变更,服务端都会实时通知到所有订阅的客户端,从而达到实时获取最新配置信息的目的。

命名服务

分布式系统中,被命名的实体通常可以是集群中的机器,提供的服务地址或远程对象等,这些都可以统称为名字,其中较为常见的就是分布式服务框架中的服务地址列表,通过使用命名服务,客户端能够根据指定名字来获取资源的实体,服务地址和提供者的信息。

下面使用zk 实现一套分布式全局唯一ID的分配机制。

随着分库分表的实现,无法依靠数据库的auto_increment 属性来唯一标识一条记录了,

常见的实现由UUID, 包含32位字符和4个短线的字符串。然而缺点是长度过长,含义不明。

通过调用zk节点创建的API接口可以创建一个顺序节点,并且在API返回值中返回这个节点的完整名字。利用这个特性,就可以借助zk 生成全局ID.

步骤是1 所有客户端根据自己任务类型,在指定类型的任务下面通过调用create() 接口来创建一个顺序节点,2 节点创建完毕后,create()节点会返回一个完整的节点名,3 客户端拿到返回值,拼上type ,就是全局唯一ID.

分布式协调/通知

分布式协调通知服务是分布式系统中不可缺少的一个环节,是将不同分布式组件有机结合的关键,基于ZK实现的分布式协调/通知功能,通常做法是不同的客户端都对zk上同一个数据节点进行watcher 注册,监听数据节点的变化,如果数据发生变化,那么所有订阅的客户端都会接收到相应的watcher 通知,并作出相应的处理。

集群管理

集群管理包括集群监控和集群控制两大块,劝着侧重对集群运行时状态的收集,后者则是对集群进行操作与控制。基于ZK的实现方法是监控系统在/clusterServers 节点上注册一个watcher监听,那么但凡动态添加的机器的操作,就会在/clusterserver节点下创建一个临时节点,这样一来,监控系统就能够实时监测到机器的变动情况。后续处理就是监控系统的业务了。

分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式,如果不同的系统或者同一个系统的不同主机之间共享了同一个或一组资源,那么访问这个资源的时候,往往需要一些互斥手段来防止彼此的干扰,以保证一致性,这种情况下,就需要分布式锁了。

排它锁

简称X锁,又称为写锁或者独占锁,如果事务T1 对数据对象O1 加上了排它锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作。即排它锁的核心是保证当前有且一个事务获得锁,并且锁被释放后,所有正在等待获取锁的事务都能被通知到。

定义锁 通过ZK上的数据节点来表示一个锁 例如/exclusive/lock

获取锁 需要获取排他锁时,所有客户端都会试图调用create()接口,创建临时子节点/exclusive/lock 。ZK会保证客户端中,最终只有一个客户端能够创建成功,那么久认为该客户端获取了锁,同时,没有获取到锁的客户端就需要到/exclusive节点上注册一个子节点变更的watcher监听,以便实时监听lock节点的变更情况。

释放锁 获取锁的客户端发生宕机,正常执行完业务逻辑后,都会将临时节点删除。都会释放锁。无论什么情况下移除了lock,zk会通知所有在/exclusive 节点上注册了子节点变更watcher监听的客户端。这些客户端接收到通知后,会重新发起分布式锁获取。

共享锁

与排它锁的根本区别在于,加排它锁后,数据对象只对一个事务可见,加共享锁后,数据对所有事务都可见。

定义锁:也是通过zk上一个数据节点来表示一个锁,是一个类似/shared/[hostname]-请求类型-序号 的临时顺序节点。(shared 表示一个共享锁)

获取锁 当需要获取共享锁时,所有客户端都回到/shared/ 这个节点下面创建一个临时顺序节点,如果是读请求,就创建/shared/192.168.0.1-R-00001 写的话就创建/shared/192.168.0.1-W-00001的节点

判断读写顺序

根据共享锁定义,不同事务都可以同时对同一个数据对象进行读取操作,而更新操作必须在当前没有任何事务进行读写操作的情况下进行。下面通过ZK来确定分布式读写顺序。

  • 创建完节点后,获取/shared/节点下所有子节点,并对该节点注册子节点变更的watcher监听。
  • 确定自己节点序号在所有子节点中的顺序
  • 对读请求,如果没有比自己序号小的子节点,或是所有比自己序号小的子节点都是读请求,那么表明自己已经成功获取共享锁,如果有比自己序号小的子节点有写请求,那么需要进行等待。对写来说,如果自己不是需要最小的子节点,就需要进入等待。
  • 接收到watcher 通知后,重复步骤1。

释放锁

分布式队列

分为两类,一种是常规的先入先出队列,另一种是等到队列元素集聚之后才统一安排执行的Barrier 模型。