分布式笔记(八)zookeeper的应用场景

zookeeper的典型应用场景
zk是一个典型的发布/订阅模式的分布式数据管理与协调框架,可以使用其进行分布式的数据发布订阅。
场景有:数据发布/订阅 负载均衡 命名服务 分布式协调/通知 集群管理 master选举 分布式锁和分布式队列

zk基于ZAB算法的实现,该框架较好的保证分布式环境中数据的一致性,是解决分布式一致性问题的利器。
hadoop hbase kafka 等分布式系统也将zk作为核心组件使用。

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

发布/订阅系统一般有两种设计模式,推push和拉pull 。在推模式中,服务端主动将数据更新发送给所有订阅的客户端,而
拉模式则是由客户端主动发起请求来获取最新数据,客户端一般通过轮询的形式来进行。
zk则是采用了推和拉相结合的方式,客户端向服务端注册自己需要关注的节点,一旦该节点的数据变更,服务端就会向客户端发送
watcher事件通知,然后客户端主动去拉取最新数据。

流程:
应用启动时,主动到zk上进行一次配置信息的获取,同时在指定节点注册一个watcher监听,如果配置发生变更,zk就会实时向所有客户端发送通知,然后客户端再去zk上读取一次配置信息。

比如说 机器列表信息,运行时的开关配置,数据库配置信息 等等。数据量比较小,数据内容在运行时可能会动态变化,集群中各机器共享,配置一致。

2.负载均衡
zk的负载均衡实现依赖于其临时节点的特性,比如说在一个服务提供者集群中,不断的有机器上线下线。机器的数量在动态变化,
那么新加入的机器,如何快速进入可用列表中,以缓解其他机器的压力。

流程:所有的机器在启动时都将自己的信息发布到指定节点,并监听。 机器意外下线后,或者新增机器后,节点的变更信息会通知给所有的客户端,那么客户端就可以决定将工作任务量进行调整,发给哪个机器。

3.命名服务
Name Service ,命名服务 ,通过命名服务 客户端应用能够指定名字来获取资源的实体,服务地址和提供者信息等,说白就是
名称-资源的映射。 比如说dubbo的服务提供者,将信息注册到节点上,消费者通过该节点就可以获取到提供者的信息。

4.分布式ID
通过zk节点api创建一个顺序节点,api返回值中会返回这个节点的完整名字。利用这个特性就会可以生成全局id.
比如说在task节点下面创建的顺序节点job ,每次创建成功返回job-00001 或者job-00002 等等,都是全局唯一的。

5.分布式协调/通知 备份容灾 HA方案
原理就是 多个客户端对同一个数据节点进行watcher注册,监听节点的变化,包括数据节点和它的子节点,如果数据节点发生变化,那么所有的订阅的客户端 都能收到相应的watcher通知,并作出相应的处理。

5.1 热备份,所有机器在启动的时候,都往一个共同节点注册临时顺序节点,完成节点创建之后,每台机器都可以获取到自己的节点和所有的节点列表,对比一下 判断自己是不是里面序号最小的,如果是,那么就将自己的状态设置为running ,其他任务机器则将自己设置为standby,这个策略叫小号优先。

设置为runing的机器可以正常运行,其他standby机器则是进入待命状态,如果running机器出现故障停止了任务执行,那么其他所有机器监听了这个节点 都会收到一个watcher事件通知,再次按小号优先策略选出下一个running机器。
ps:这里有个优化点,只需要通知自己下一个节点就好了,然后下一个节点拉取所有的节点 判断一下自己是不是当前最小的,是的就接替running,并不需要通知所有的机器。比如说创建了节点序号2的机器监听节点序号为1的机器,节点序号3的机器监听节点序号2的机器,这样如果1挂了,2就会收到通知,进而继续运行。

需要注意的是 因为切换了机器,所以新的机器需要知道原来的机器正在处理的任务,因此,原来的机器需要将自己运行时的上下文状态保留给standby机器,因此 需要将任务信息也写入到zk上。

5.2 互为主备,当一个正常的机器发现其他机器挂了的时候,将那台机器未完成的任务拿来做。
分布式笔记(八)zookeeper的应用场景

分布式笔记(八)zookeeper的应用场景

6.心跳检测,工作进度汇报,系统调度
1.一个机器通过监听另一个机器注册在zk上的临时节点就可以知道另一台机器的存活情况
2.任务机器将自己的任务进度写到zk的临时节点上,其他系统能够通过该节点获取到任务的进度
3.通过修改zk上节点的内容,使得监听了这些节点的机器能够收到通知,进而可以实现通知调度

7.集群管理
使用zk来维护监控集群中各个机器的状态,比如说监控多少台机器在线,对运行时状态数据采集,对机器进行上下线操作。
比如说日志收集系统, 其中分为两类角色,日志源机器(应用所在机器) ,收集器机器(进行日志收集服务的机器)
因为日志源机器在不断的上下线,收集器机器也会动态扩容或者变化,那么就会有一个问题,如何快速合理动态的为每个收集器
分配对应的日志源机器。 在zk中,注册一个收集器根节点,所有收集器机器在这个节点下又创建自己机器名称的子节点,
将日志源机器写入到这些收集器机器节点下,这样每个收集器机器都可以从自己的子节点中获取到需要收集日志的源机器。

这个节点类型是持久的,如果是临时的,那么当收集器挂了之后,该节点下的源机器节点也被删了。

如果某个收集器节点挂了,那么日志系统就将分配给这个机器的源日志机器节点分配给其他收集器节点。

8.Master选举
有时候需要在集群中选出一台master,且需要master挂了的时候快速选出新的master, 就可以用zk完成。
原理是 zk确保一个同一个节点只能成功创建一次,那么成功创建的就是master,其他机器则订阅这个节点,
一旦这个临时节点失效,其他机器会收到watcher事件通知,那么就可以再次竞选新的master.

9.分布式锁
1.排他锁
原理和master选举类似,都是多个机器同时创建同一个节点,如果成功的就是获取到了锁,失败的进入等待,
锁释放的时候,会删除掉这个临时节点(或者获取锁的机器宕机),此时其他机器会收到watcher事件通知,那么这个时候,唤醒等待的线程,重新去竞争锁。

2.共享锁
共享锁即读写锁,读读不冲突,读写冲突
需要获取共享锁时,客户端在sharlock节点下创建一个新的临时顺序节点,如果是读请求,那么创建Rnode ,如果是写请求,创建Wnode ,然后进入以下流程
1.获取sharlock下的所有子节点,并注册子节点变更的watcher监听。
2.确定自己的节点的在所有子节点的顺序
3.如果是读请求,那么如果自己前面没有写请求,那么就获取读锁成功,否则进入等待
4.如果是写请求,那么如果自己的序号最小,前面没有任何请求,那么就获取写锁成功,否则进入等待
5.执行完之后,需要删除掉自己的节点,然后其他节点收到watcher通知户,进入到1

这里有个优化点,每个机器真正关心的是自己能不能执行,因此 只需要将通知发给能执行的机器就行了。
比如说 如果有1000台机器竞争,那么就会zk中产生大量的watcher事件,但是其中只有一台是有效的。

因此,读节点只要监听自己前面的写节点就可以,如果自己之前的写节点结束,那么自己收到这个通知就可以运行。
写节点则是监听自己前面的一个节点,只要自己前面没有节点了,自己就能运行。

10.分布式队列
1.先入先出的队列
思路:每个进程创建临时顺序节点,然后获取所有子节点,然后看自己是不是序号最小的,如果是则出队,否则监听比自己小的最大节点,如果那个节点被删除,收到watcher事件之后,看下自己是不是最小的,如果是 则出队。

2.当队列元素聚齐,统一执行的barrier
多个线程到某个点,一起执行。
思路:
在公共节点barrier写入内容 ,比如说10 ,然后各个线程在这个节点下创建子节点,并监听,
当监听到watcher事件的时候,就拉取节点列表和barrier的内容,当内容中的n等于节点个数时就执行。

zk在大型分布式系统中的应用
1.在hadoop中实现HA(high availability) 高可用,主从备份,用于解决单点问题
2.Hbase中 zk用于监听各个节点的状态
3.kafka用zk来管理Broker的状态 动态表示broker机器是否可用 ,也用zk实现broker的动态负载均衡,提供者和消费者的动态负载均衡,同时也用zk来记录消费的进度。
4.dubbo的注册中心也基于zk实现,/dubbo是dubbo在zk注册的根节点,其下有/dubbo/xxxService等服务节点
/dubbo/xxxService/provider 提供者列表
/dubbo/xxxService/consumers消费者列表
以上都是临时节点,当服务提供者或者消费者宕机之后,该临时节点就会从zk上删除,订阅了这些节点的机器就会收到watcher
事件,进而更新配置。

5.canal server的主备设计
canal server集群中,都启动阶段去创建同一个节点,创建成功的就是running,失败的 则进入standby状态,
并监听runing,如果runing挂了,收到watcher通知,就获取所有子节点列表,如果自己是序号最小的,那么就将
自己的状态变更为runing.

canal client 的HA设计,canal client 启动的时候读取running状态的server节点,获取到server的信息,并开始消费。
canal server会将消费进度也记录到zk中。
如果监听到runing节点挂了,那么 就拉取新的节点列表,找到新的runing节点,并订阅其中新的server.
如果canal client 重启或者变化,就会从zk读取到之前的消费记录,重新开始消费。