一、Zookeeper介绍
Zookeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布与订阅,通过对Zookeeper中丰富的数据节点类型进行交叉使用,配合Watcher事件通知机制,可以非常方面地构建一系列分布式应用都会涉及到的核心功能,如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等。
二、数据发布/订阅
发布者将数据发布到Zookeeper的一个或一系列的节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。一般分为推(Push)模式和拉(Pull)模式。在推模式中,服务端主动将数据发送诶所有订阅的客户端,而拉模式则由客户端主动发起请求获取最新的数据。
在平常应用系统中,系统需要使用一些通用的配置信息,例如机器列表信息、数据库配置等等,这些配置信息通常具备以下三个特性:
- 数据量比较小
- 数据内容在运行时会动态的变化
- 集群中各机器共享,配置一致
三、负载均衡
通常在使用过程中,在Zookeeper上创建一个节点进行域名配置,如下图
每一个应用都可以创建一个属于自己的节点作为域名配置的根节点,然后将自己的域名配置行去,到时候,根据域名就可以找到这台服务器对应的IP。
四、命名服务
命名服务是分布式系统中比较常见的一类场景,被命名为实体通常可以是集群中的机器,提供的服务地址或者远程对象等--这些都可以统称它们我名字,较为常见的是分布式服务框架(RPC、RMI)中的服务地址列表,通过命令服务,客户端就能够根据这些指定的名字来获取资源实体、服务器信息等。
通过调用Zookeeper节点创建API可以创建一个顺序节点,并且在该API的返回值中会返回当前这个节点的完整名字,利用这个特性,就可以额借助Zookeeper来生成全局的唯一ID。
五、分布式协调通知
分布式协调/通知服务是分布式系统中不可缺少的一个环节,是将不同的分布式组件连接起来的关键。而对于一个在多台机器上部署的系统来说,通常需要一个协调者来控制整个系统的运行流程,例如分布式事务的处理,机器间的相互协调等。引入这样的一个协调者,可以大大的减少系统之间的耦合性,能够显著的提高系统的可扩展性。
Zookeeper中的Watcher注册和异步通知机制,能够很好实现分布式环境下的不同机器,甚至是不同系统之间的协调通知,从而实现对数据变更的实时处理。就与Zookeeper实现分布式协调和通知功能,通常的做法是不同的客户端都对Zookeeper上的同一个数据节点进行Watcher注册,监听数据节点的变化,如果节点数据发生变化,那么所有订阅的客户端都可以接收到Watcher通知,并作出相应的处理。
六、集群管理
包括集群监控和集群控制,前者侧重于对于集群实时运行状态采集,后者则是对于集群进行操作和控制。
在集群中,机器完成注册和任务分发之后,还要考虑这些机器随时都有挂掉的可能性,针对这种情况,需要一个收集器的装填汇报机制,每一台机器在Zookeeper上创建一个自己的节点之后,还需要再子节点上创建一个自己的状态节点,定时的向临时节点写入自己的状态信息,也就是一个心跳检测机制。
七、Master选举
Master选举时一个分布式系统中非常常见的场景,分布式最核心的特性就是能够将具有独立计算能力的系统单元部署在不同的机器上,构成一个完整的分布式系统。
在分布式的系统中,Master通常用来协调集群中的其他系统单元,具有对分布式系统状态的决定权。例如:在一些读写分离的系统中,客户端的写请求往往是由Master来处理的,而在另外的一些场景中,Master常常负责一些复杂的逻辑,并将这些处理结果同步给系统的其他单元。
八、分布式锁
分布式锁是控制系统之间同步访问共享资源的一种方式。如果不同的系统或者同一个系统的不同主机之间共享了一个或者一组资源,那么访问这些资源的时候,往往需要通过一些互斥的手段来访问之间的干扰,一保证一致性,这种情况下就需要使用分布式锁。Zookeeper的分布式锁分为两种,排他锁和共享锁。
排他锁(Exclusive Locks)
定义:
又称写锁欧哲独占锁,如果事务T1对数据对象O1加上拍他锁,那么在整个枷锁期间,只允许事务T1对O1进行读取和更新操作,通过Zookeeper上的一个数据节点就可以表示一个锁。
获取锁:
在需要获取排他锁的时候,所有的客户端都会尝试调用create()接口,Zookeeper会保证所有的客户端,最终只会有一个客户端能够创建成功,那么久可以认为该客户端获取了锁。
释放锁:
在下面两种情况都可能释放锁
- 获取锁的客户端发生宕机,Zookeeper上临时节点就会被移除。
- 正常业务逻辑执行完成后,客户端会主动删除创建的临时节点。
共享锁(Shared Locks)
定义:
又称读锁,如果事务T1对O1加上了共享锁,那么当前事务只能对O1进行读取操作,其他事务也只能对整个数据对象加上共享锁-直到该数据对象上的所有共享锁都被释放。
和排他锁一样,是通过Zookeeper上的数据节点表示一个锁
获取锁:
在需要获取共享锁时,所有的客户端都会到/shared_locks整个节点下面创建一个临时顺序节点,如果是读请求,就创建R-000000001节点,如果是写请求,就窗口W-000000001节点 。
根据共享锁的定义,不同的事务,可以对同一个数据对象进行读取操作,而更新操作必须在当前没有任务事务进行读写的情况下进行。对节点进行分布式读写顺序大概分为四个步骤:
- 创建完节点之后,获取/shared_locks下面的所有子节点,并对该节点注册子节点变更的Watcher监听
- 确定自己的节点需要在所有子节点中的顺序。
- 对于读请求,如果没有比自己序号小的子节点,或者所有比自己序号小的节点都是读请求,那么表明自己已经成功获取共享锁。如果比自己序号小的节点有写请求,那么就进行等待。
- 接收到Watcher通知,重复步骤1
释放锁:
在下面两种情况都可能释放锁
- 获取锁的客户端发生宕机,Zookeeper上临时节点就会被移除。
- 正常业务逻辑执行完成后,客户端会主动删除创建的临时节点。
九、分布式队列
队列是先进先出,实现和共享锁非常相似,客户端都会到/queue_fifo节点下面创建一个临时顺序节点
创建完之后,根据如下四个步骤觉得执行顺序:
- 通过调用getChildren()接口来获取/queue_fifo节点下面的所有子节点,即获取队列中的所有元素。
- 确定自己的节点在所有子节点中的顺序。
- 如果自己不是序号最小的节点,就需要等待,同时向比自己序号小的最后一个节点注册Watcher监听。
- 接收到监听之后,重复步骤1