目录
1. 数据发布订阅——Watch机制
如:全局配置信息(配置更新频繁)
- 推拉结合
客户端向服务器注册自己关注的节点、一旦该节点的数据发生变更,那么服务器就向相应的客户端发送Watcher事件通知,客户端收到通知之后,需要主动到服务端获取最新的数据。
2. 负载均衡——Watch机制
如:动态DNS(DDNS)动态域名负载均衡。
DNS
域名和IP地址一一映射,在实际开发中,往往使用本地HOST绑定来实现域名解析。缺陷:
- 域名变更,则需要在每个机器上逐个变更;
- 机器上线,需要在每台机器上去绑定域名。
DDNS
在Zookeeper上创建一个个 域名节点 ,将ip列表配置上去。
- 域名注册
每个服务提供者在启动的过程中,都注册自己的域名、ip等信息,然后DDNS自动根据域名将ip信息写入相应的节点中;
- 域名解析
由应用自己负责,通常先从域名节点获取一份ip地址的配置,进行自我解析。同时每个应用还会在域名节点注册一个数据变更Watcher监听,以便及时接收到域名变更的通知。
- 域名变更
zookeeper会向订阅的客户端发送这个事件,应用收到通知后,会再次进行域名配置的获取。
3.命名服务(分布式全局ID)——顺序节点
如果是单库单表系统,那么我们可以使用数据库自带的auto_increment属性。
对于分布式的应用,有一种方式是UUID:基于硬件、时间、随机数等特征生成。包含32位字符+4个短线,缺点是长度太长、含义不明。
Zookeeper实现唯一ID
利用Zookeeper提供的创建 顺序节点 的接口,对客户端的调用zookeeper会自动以后缀的形式在子节点上添加序号并返回。
4. 集群管理——临时节点 + Watch机制
目标:
- 希望知道当前集群中有多少机器在工作;
- 对集群中每台机器的运行时状态进行收集;
- 对集群中机器进行上下线操作;
客户端在zookeeper的一个节点注册临时节点,一旦会话失效,那么该临时节点也被自动清除。
5. Master选举——并发安全
场景:广告投放系统集群中,商品或广告投放id往往需要从海量数据中计算得到,通常是一个耗费IO与CPU的过程。我们需要让集群中的部分或者一台主机去处理,然后共享给整个集群中其他所有机器。
实现:Client集群每天定时通过zookeeper实现master选举,master节点计算出结果之后,存储到内存/数据库中,同时通知其他节点共享计算结果。
Master节点选取
-
关系型数据库的主键:所有节点向数据库插入一条相同主键的记录,哪个成功了哪个就是master。
缺陷:选出的master如果挂了,其他节点不知道。 -
zookeeper中有 多个客户端创建同一个节点,只有一个能成功 。其他失败的节点则注册一个watcher,用于监控当前master是否存活,一旦master挂了其余节点将重新选取。
关于并发创建节点,只有一个能成功的原因:
对于Zookeeper来说,写请求也就是事务请求,客户端不论连接到哪个集群节点,最终都会被转发到Leader节点执行,因此可以顺序处理。
6. 分布式锁——临时顺序节点 + Watch机制
6.1 排它锁
获取锁
- 客户端通过调用create创建同一个 临时节点 ,调用成功的那个客户端代表获取到了锁。
- 同时没有获取到锁的客户端,在该节点上注册watcher监听,以便监听到lock节点的变更情况。
释放锁
在获取锁时,客户端创建的是临时节点,有两种情况下都可能释放锁:
- 当前获取锁的客户端发生宕机,那么zookeeper上这个临时节点将被移除
- 正常执行完业务逻辑之后,客户端就会主动将自己创建的节点删除
6.2 共享锁
获取锁
- 客户端调用create创建一个 临时顺序节点
- 创建完节点之后,获取当前节点下的所有子节点,并注册 watcher监听所有子节点列表
- 确定自己的节点序号在子节点中的顺序
- 如果是读请求,判断是否能成功获取锁:
- 没有比自己序号小的节点
- 比自己序号小的节点都是读请求
- 如果是写请求,判断是否能成功获取锁:
- 自己是最小的子节点
- 如果是读请求,判断是否能成功获取锁:
- 接收到watcher通知之后,重新获取该节点下所有子节点,重复上述步骤
释放锁
与排它锁的逻辑一样
共享锁的缺陷——羊群效应
当集群规模足够大,获取锁的客户端完成业务之后释放锁,其余所有节点都「收到watcher通知」、「子节点列表获取」。
然而只有一台机器检查到自己是序号最小的机器可以进行操作,其余节点仍然继续等待。缺陷在于客户端收到过多与自己并不相关的事件通知。
如果同一时间多个节点完成事务或者是事务中断,引起节点消失,zookeeper服务器就会 短时间内向其余客户端发送大量的时间通知 ——这就是所谓的羊群效应。
- 改进:客户端只需要关注比自己序号小的那个节点,而不需要关注全局的子列表变更情况。
改进后的共享锁
获取锁
- 客户端调用create创建临时顺序节点,调用getChildren获取所有已经创建的子节点列表,注意,这里不注册任何watcher
- 如果无法获取共享锁,向自己的目标节点注册watcher。
这里的目标节点:- 读请求是比自己序号小的那个写节点;
- 写请求是前一个节点。
- 等待watcher通知
7. 分布式队列——临时顺序节点 + Watch机制
应用:FIFO先进先出队列
与共享锁的实现类似:注册临时顺序节点、确定自己的顺序,如果不是最小的,则监听最后一个节点。