Zookeeper经典应用场景实战
ZK的不足之处
- watcher检测是一次性的,每次触发之后都需要重新注册
- 会话超时之后没有实现重连机制
- 异常处理繁琐
- 仅提供byte数组类型的接口,没提供java实体序列化级接口
- 创建节点时如果抛出异常,需要自行检查节点是否存在
- 无法实现级联删除节点
ZK在分布式命名服务中的实战
分布式API目录
- 借助于ZK的树形分成结构为分布式系统中各种API接口服务的名称、链接地址,提供类似JNDI(Java命名和目录接口)中的文件系统的功能
- 在Dubbo中使用ZK维护全局服务接口API的地址列表,大致思路如下
- 服务提供者启动时,向ZK上的指定节点写入自己的API地址
- 服务消费者启动时,订阅节点下的服务提供者的URL地址获取所有服务提供者的API
分布式节点命名
- 常规情况可以采用DB的自增特性、机器的MAC地址、IP进行分布式节点命名维护
- 使用ZK的持久顺序节点的顺序特性进行分布式节点命名维护,基本流程如下
- 启动节点服务,连接ZK,检查命名服务根节点是否存在,如果不存在,就创建系统的根节点
- 在根节点下创建一个临时顺序节点,取回该节点的编号把它作为分布式系统中的节点ID,如果临时节点太多,可以根据需要删除临时顺序节点
分布式ID生成器
- 在分布式环境下,全局唯一ID系统需要满足全局唯一、高可用
常见实现方案
- Java的UUID
- 基于Redis的原子操作incr和incrby生产更全局唯一ID
- 基于Twitter的雪花算法
- 4个部分
- 首位占用1bit,值为0,无实际作用
- 时间戳占用41bit,精确到ms,可以容纳约69年的时间
- 工作机器id占用10bit,最多可以容纳1024个节点
- 序列号占用12bit,这个值在同一ms同一节点上从0开始不断累加,最多可以累加到4095,在工作节点1024个满载情况下,同一毫秒最多可生成1024 * 4095个ID,满足绝大多数并发场景
- 优点
- 生成ID不依赖于数据库,完全在内生成,高性能、高可用性
- 容量大,每秒可以生成几百万个ID
- ID呈递增,后续插入数据库索引树,性能高
- 缺点
- 依赖于系统时钟的一致性,如果出现系统
时钟回拨
,可能会造成ID冲突或者ID乱序- 在启动之前,如果这台机器的系统时间回拨过,那么有可能出现ID重复的危险
- 依赖于系统时钟的一致性,如果出现系统
- 4个部分
- 基于ZK的顺序节点生成全局唯一ID
- 基于MongoDB的object_id,每插入一条记录会自动生成全局唯一的一个"_id"字段值,它是一个12字节的字符串,可以作为分布式系统中全局唯一的ID
分布式队列
- ZK作为一个小文件分布式管理系统,对于吞吐量不高的小型系统可以作为分布式队列使用
设计思路
- 创建队列根节点: 在ZK中创建一个持久节点,用作队列的根节点,所有队列元素的节点放在这个根下
- 实现入队: 当需要将一个入队时,可以在队列的根下创建一个临时顺序节点,节点的数据可以包含队列元素的信息
- 实现出队: 当需要从队列中取出元素时,执行操作
- 获取根下所有子节点
- 找到最小序号的节点
- 获取该节点数据
- 删除该节点数据
- 返回节点数据
分布式锁
- 在单体的应用开发场景中涉及并发同步的时候,往往采用synchronized或lock机制来解决多线程之间的同步问题
- 在分布式集群工作的开发场景中,就需要
分布式锁
基于数据库的分布式锁
- db操作性能较差,并且有锁表的风险,一般不考虑
- *可以利用数据库的唯一索引来实现,唯一索引天然具有排他性
问题:
基于数据库实现分布式锁存在什么问题- 当遇到数据库宕机时,无法进行及时删除导致死锁问题
- 无法实现监听机制,当释放锁时无法通知其他线程去获取锁
- 当遇到数据库宕机时,无法进行及时删除导致死锁问题
基于Redis的分布式锁
- 适用于并发量很大、性能要求很高而且可靠性问题可以通过其他方案去弥补的场景
- 使用setnx命令实现分布式锁,为了避免服务宕机死锁需要加一个过期时间可以使用set ...ex ...nx
基于ZK的分布式锁
- 适用于高可用,而并发量不太高的场景
基于ZK非公平锁设计思路
- 多个线程在同一时刻去创建临时节点,只能有一个线程会成功,未创建成功的线程会去监听该节点,该节点一旦被删除,这些等待的线程会继续去竞争
问题:
非公平锁的机制会有什么问题- 当竞争激烈的时候,会导致性能下降,出现
惊群效应
- 因为只有一个线程能成功抢到锁,如果在大量线程的情况下,由于这些未抢到锁的线程都会去监听那个锁节点,一旦通知释放之后,都会一起去竞争,导致性能损耗
- 当竞争激烈的时候,会导致性能下降,出现
基于ZK公平锁设计思路
- 会先创建一个
容器节点
,需要持锁的线程会在该容器节点下去创建临时顺序节点
,每个子节点都会监听它前面的兄弟节点,当线程删除序号最小的节点释放锁后,后面监听该节点的节点会成为最小序号的节点保证当前线程持锁 问题
:为什么公平锁创建的是容器节点来保持临时顺序节点- 因为容器节点会定期检查有无子节点,如果没有会删除,可以有效避免手动删除的操作
服务的注册与发现
基于ZK实现注册中心
- 优点
- 高可用性: ZK是一个高可用分布式系统,可以通过配置多个服务实例来提供容错能力,如果其中一个实例出现故障,其他实例仍然能继续提供服务
- 强一致性: ZK保证了数据的强一致性,当一个写操作完成时,所有服务都具有相同的数据视图,所以这样可以确保所有客户端看到的服务状态是一致的
- 实时性: ZK的watch机制允许客户端监听节点的变化,当服务上线、下线时,客户端会实时收到通知,这样确保服务动态发现
- 缺点
- 性能限制: ZK在大量的读写操作或大规模集群下会出现性能瓶颈