zookeeper,通俗的来讲,他目前在各种分布式系统中充当大脑的角色,给各个应用管理配置文件,提供应用的分布式锁,提供应用命名,提供组服务这四个功能.
1. 管理配置文件(维护配置信息)
A. zookeeper提供的这个配置管理,就是把这公用的配置文件提取出来放到一个地方,对这个地方(目录节点)进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好,项目不需要重启.
B. 标准的key value的配置文件结构
C. 基于web页面的配置文件的增删改查
D. 同一个app(应用)下多个配置文件
E. 版本管理,当前版本,历史版本管理
F. 数据持久化保存
H. 客户端watcher的实现
2. 提供应用命名
A. 当集群的时候,相同的一个服务有很多个提供者,这些提供者启动时,提供者服务器的相关信息,包括服务接口,地址,端口等一下连接提供者的信息注册到zookper中,当消费者要消费某服务的时候,从zookeeper中拿改服务的所有提供者信息目录,再根据dubbo的负载均衡机制从地图中选择一个提供者。
3. 分布式锁(分布式同步)
**A**. 进程需要访问共享数据时, 就在"/locks"节点下创建一个sequence类型的子节点, 称为thisPath. 当thisPath在所有子节点中最小时, 说明该进程获得了锁. 进程获得锁之后, 就可以访问共享资源了. 访问完成后, 需要将thisPath删除. 锁由新的最小的子节点获得.
**B**. 有了清晰的思路之后, 还需要补充一些细节. 进程如何知道thisPath是所有子节点中最小的呢? 可以在创建的时候, 通过getChildren方法获取子节点列表, 然后在列表中找到排名比thisPath前1位的节点, 称为waitPath, 然后在waitPath上注册监听, 当waitPath被删除后, 进程获得通知, 此时说明该进程获得了锁.
**C**. lock操作过程
a. 首先为一个lock场景,在zookeeper中指定对应的一个根节点,用于记录资源竞争的内容
b. 每个lock创建后,会lazy在zookeeper中创建一个node节点,表明对应的资源竞争标识。 (小技巧:node节点为EPHEMERAL_SEQUENTIAL,自增长的临时节点)
c. 进行lock操作时,获取对应lock根节点下的所有字节点,也即处于竞争中的资源标识
d. 按照Fair竞争的原则,按照对应的自增内容做排序,取出编号最小的一个节点做为lock的owner,判断自己的节点id是否就为owner id(锁有者),如果是则返回,lock成功。
e. 如果自己非owner id,按照排序的结果找到序号比自己前一位的id,关注它锁释放的操作(也就是exist watcher),形成一个链式的触发过程。
**D**. unlock操作过程
a. 将自己id对应的节点删除即可,对应的下一个排队的节点就可以收到Watcher事件,从而被唤醒得到锁后退出
**E**. 其中的几个关键点
a. node节点选择为EPHEMERAL_SEQUENTIAL很重要
1). 自增长的特性,可以方便构建一个基于Fair特性的锁,前一个节点唤醒后一个节点,形成一个链式的触发过程。可以有效的避免"惊群效应"(一个锁释放,所有等待的线程都被唤醒),有针对性的唤醒,提升性能。
2). 选择一个EPHEMERAL临时节点的特性。因为和zookeeper交互是一个网络操作,不可控因素过多,比如网络断了,上一个节点释放锁的操作会失败。临时节点是和对应的session挂接的,session一旦超时或者异常退出其节点就会消失,类似于ReentrantLock中等待队列Thread的被中断处理.
**F**. 注意
a. 使用EPHEMERAL会引出一个风险:在非正常情况下,网络延迟比较大会出现session timeout,zookeeper就会认为该client已关闭,从而销毁其id标示,竞争资源的下一个id就可以获取锁。这时可能会有两个process同时拿到锁在跑任务,所以设置好session timeout很重要。
b. 同样使用PERSISTENT同样会存在一个死锁的风险,进程异常退出后,对应的竞争资源id一直没有删除,下一个id一直无法获取到锁对象。
4. 提供组服务
A. 服务器上的服务注册,或者获取服务,都是在zookeeper上操作的,所以所谓的提供组服务也就包括创建组、加入组成员、列出组成员和删除组成员。对于这些服务,主要通过zookeeper心跳机制,它会去检测与其连接的一些服务器的数量,以及信息。什么时候连上zookeeper,或什么时候断开都有其心跳机制完成。