zookeeper应用及深入进阶


前言

ZooKeeper最为主要的使⽤场景,是作为分布式系统的分布式协同服务。ZooKeeper对分布式系统的协调,使⽤的是共享存储方式。其设计⽬标是将那些复杂的且容易出错的分布式⼀致性服务封装起来,构成⼀个⾼效可靠的原语集,并以⼀些简单的接⼝提供给⽤户使⽤。它实现诸如数据订阅/发布、负载均衡、命名服务、集群管理、分布式锁和分布式队列等功能。(笔者希望在阅读此篇文章前,可以有一定的zookeeper基础。。。🤣/font>


一、应用场景

1、数据发布/订阅

数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中⼼,顾名思义就是发布者将数据发布到ZooKeeper的⼀个或⼀系列节点上,供订阅者进⾏数据订阅,进⽽达到动态获取数据的⽬的,实现配置信息的集中式管理和数据的动态更新。

ZooKeeper 采⽤的是推拉相结合的⽅式:客户端向服务端注册⾃⼰需要关注的节点,⼀旦该节点的数据发⽣变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。

举个🌰:系统中需要使⽤⼀些通⽤的配置信息,例如机器列表信息、运⾏时的开关配置、数据库配置信息等。如何方便的进行配置管理?

  1. 我们可以在Zookeeper上选取⼀个数据节点⽤于配置信息的存储
  2. 集群中每台机器在启动初始化阶段,⾸先会从上⾯提到的ZooKeeper配置节点上读取数据库信息,同时,客户端还需要在该配置节点上注册⼀个数据变更的 Watcher监听,⼀旦发⽣节点数据变更,所有订阅的客户端都能够获取到数据变更通知。
  3. 在系统运⾏过程中,可能会出现需要进⾏数据库切换的情况,这个时候就需要进⾏配置变更。借助ZooKeeper,我们只需要对ZooKeeper上配置节点的内容进⾏更新,ZooKeeper就能够帮我们将数据变更的通知发送到各个客户端,每个客户端在接收到这个变更通知后,就可以重新进⾏最新数据的获取。

2、集群管理

随着分布式系统规模的⽇益扩⼤,集群中的机器规模也随之变⼤,那如何更好地进⾏集群管理也显得越来越重要了。所谓集群管理,包括集群监控与集群控制两⼤块,前者侧重对集群运⾏时状态的收集,后者则是对集群进⾏操作与控制。

举个🌰:在⽇常开发和运维过程中,我们经常会有类似于如下的需求:

  • 如何快速的统计出当前⽣产环境下⼀共有多少台机器
  • 如何快速的获取到机器上下线的情况
  • 如何实时监控集群中每台主机的运⾏时状态

如何用zookeeper进行解决?

  1. 注册收集器机器
    典型做法是在ZooKeeper上创建⼀个节点作为收集器的根节点,每个收集器机器在启动的时候,都会在收集器节点下创建⾃⼰的节点。

  2. 任务分发
    待所有收集器机器都创建好⾃⼰对应的节点后,系统根据收集器节点下⼦节点的个数,将所有⽇志源机器分成对应的若⼲组,然后将分组后的机器列表分别写到这些收集器机器创建的⼦节点上去。这样⼀来,每个收集器机器都能够从⾃⼰对应的收集器节点上获取⽇志源机器列表,进⽽开始进⾏⽇志收集⼯作。

  3. 状态汇报
    完成收集器机器的注册以及任务分发后,我们还要考虑到这些机器随时都有挂掉的可能。因此,针对这个问题,我们需要有⼀个收集器的状态汇报机制:每个收集器机器在创建完⾃⼰的专属节点后,还需要在对应的⼦节点上创建⼀个状态⼦节点,每个收集器机器都需要定期向该节点写⼊⾃⼰的状态信息。我们可以把这种策略看作是⼀种⼼跳检测机制,通常收集器机器都会在这个节点中写⼊⽇志收集进度信息。⽇志系统根据该状态⼦节点的最后更新时间来判断对应的收集器机器是否存活。

  4. 动态分配
    如果收集器机器挂掉或是扩容了,就需要动态地进⾏收集任务的分配。在运⾏过程中,⽇志系统始终关注着该节点下所有⼦节点的变更,⼀旦检测到有收集器机器停⽌汇报或是有新的收集器机器加⼊,就要开始进⾏任务的重新分配。⽆论是针对收集器机器停⽌汇报还是新机器加⼊的情况,⽇志系统都需要将之前分配给该收集器的所有任务进⾏转移。

3、分布式锁

3.1 排他锁

排他锁(Exclusive Locks,简称 X 锁),⼜称为写锁或ᇿ占锁,是⼀种基本的锁类型。如果事务 T1对数据对象 O1加上了排他锁,那么在整个加锁期间,只允许事务 T1对 O1进⾏读取和更新操作,其他任何事务都不能再对这个数据对象进⾏任何类型的操作——直到T1释放了排他锁。

从上文不难看出,排他锁的核⼼是如何保证当前有且仅有⼀个事务获得锁,并且锁被释放后,所有正在等待获取锁的事务都能够被通知到。

下面一起来看下ZooKeeper实现排他锁:

  1. 定义锁
    通过 ZooKeeper上的数据节点来表示⼀个锁,例如/exclusive_lock/lock节点就可以被定义为⼀个锁,如图:
    在这里插入图片描述

  2. 获取锁
    在需要获取排他锁时,所有的客户端都会试图通过调⽤ create()接⼝,在/exclusive_lock节点下创建临时⼦节点/exclusive_lock/lock。ZooKeeper 会保证在所有的客户端中,最终只有⼀个客户端能够创建成功,那么就可以认为该客户端获取了锁。同时,所有没有获取到锁的客户端就需要到/exclusive_lock 节点上注册⼀个⼦节点变更的Watcher监听,以便实时监听到lock节点的变更情况。

  3. 释放锁
    在“定义锁”部分,我们已经提到,/exclusive_lock/lock 是⼀个临时节点,因此在以下两种情况下,都有可能释放锁。
    · 当前获取锁的客户端机器发⽣宕机,那么ZooKeeper上的这个临时节点就会被移除。
    ·正常执⾏完业务逻辑后,客户端就会主动将⾃⼰创建的临时节点删除。 ⽆论在什么情况下移除了lock节点,ZooKeeper都会通知所有在/exclusive_lock节点上注册了⼦节点变更Watcher监听的客户端。这些
    客户端在接收到通知后,再次重新发起分布式锁获取,即重复“获取锁”过程。

整体流程如图:
在这里插入图片描述

3.2 共享锁

共享锁(Shared Locks,简称S锁),⼜称为读锁,同样是⼀种基本的锁类型。
如果事务T1对数据对象O1加上了共享锁,那么当前事务只能对O1进⾏读取操作,其他事务也只能对这个数据对象加共享锁——直到该数据对象上的所有共享锁都被释放。
一起来看下实现步骤:

1. 定义锁
是通过 ZooKeeper 上的数据节点来表示⼀个锁,是⼀个类似于
“/shared_lock/[Hostname]-请求类型-序号”的临时顺序节点,例如/shared_lock/host1-R-0000000001,那么,这个节点就代表了⼀个共享锁,如图所示:
在这里插入图片描述
2. 获取锁
在需要获取共享锁时,所有客户端都会到/shared_lock 这个节点下⾯创建⼀个临时顺序节点,如果当前是读请求,那么就创建例如/shared_lock/host1-R-0000000001的节点;如果是写请求,那么就创建例如/shared_lock/host2-W-0000000002的节点。
判断读写顺序
 1. 创建完节点后,获取/shared_lock节点下所有⼦节点,并对该节点变更注册监听。
 2. 确定⾃⼰的节点序号在所有⼦节点中的顺序。
 3. 对于读请求:若没有⽐⾃⼰序号⼩的⼦节点或所有⽐⾃⼰序号⼩的⼦节点都是读请求,那么表明⾃⼰已经成功获取到共享锁,同时开始执⾏读取逻辑,若有写请求,则需要等待。对于写请求:若⾃⼰不是序号最⼩的⼦节点,那么需要等待。
 4. 接收到Watcher通知后,重复步骤1

3. 释放锁
同独占锁一致。

二、深入进阶

1.ZAB协议

ZAB协议并不像Paxos算法那样 是⼀种通⽤的分布式⼀致性算法,它是⼀种特别为zookeeper专⻔设计的⼀种⽀持崩溃恢复的原⼦⼴播协议。

核⼼是定义了对于那些会改变Zookeeper服务器数据状态的事务请求的处理⽅式。
即:所有事务请求必须由⼀个全局唯⼀的服务器来协调处理,这样的服务器被称为Leader服务器,余下的服务器则称为Follower服务器,Leader服务器负责将⼀个客户端事务请求转化成⼀个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器,之后Leader服务器需要等待所有Follower服务器的反馈,⼀旦超过半数的Follower服务器进⾏了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前⼀个Proposal进⾏提交。
在这里插入图片描述

2.服务器角色

Leader
Leader服务器是Zookeeper集群⼯作的核⼼,其主要⼯作有以下两个:
  (1) 事务请求的唯⼀调度和处理者,保证集群事务处理的顺序性。
  (2) 集群内部各服务器的调度者。
  
Follower
Follower服务器是Zookeeper集群状态中的跟随者,其主要⼯作有以下三个:
  (1) 处理客户端⾮事务性请求(读取数据),转发事务请求给Leader服务器。
  (2) 参与事务请求Proposal的投票。
  (3) 参与Leader选举投票。

Observer
Observer服务器在⼯作原理上和Follower基本是⼀致的,对于⾮事务请求,都可以进⾏ᇿ⽴的处理,⽽对于事务请求,则会转发给Leader服务器进⾏处理。和Follower唯⼀的区别在于,Observer不参与任何形式的投票,包括事务请求Proposal的投票和Leader选举投票。简单地讲,Observer服务器只提供⾮事务服务,通常⽤于在不影响集群事务处理能⼒的前提下提升集群的⾮事务处理能⼒。

3.服务器启动

整体架构如图:
在这里插入图片描述
大致分为以下五步:
 1. 配置⽂件解析
 2. 初始化数据管理器
 3. 初始化⽹络I/O管理器
 4. 数据恢复
 5. 对外服务

4.Leader选举

Leader选举是zookeeper最重要的技术之⼀,也是保证分布式数据⼀致性的关键所在。
当Zookeeper集群中的⼀台服务器出现以下两种情况之⼀时,需要进⼊Leader选举。
  (1) 服务器初始化启动。
  (2) 服务器运⾏期间⽆法和Leader保持连接(Leader挂掉了)。

4.1 服务器启动时期的Leader选举

  1. 每个server发出一个投票
    由于是初始情况,Server1(假设myid为1)和Server2假设myid为2)都会将⾃⼰作为Leader服务器来进⾏投票,每次投票会包含所推举的服务器的myid和ZXID,使⽤(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各⾃将这个投票发给集群中其他机器。
  2. 接受来⾃各个服务器的投票
    集群的每个服务器收到投票后,⾸先判断该投票的有效性,如检查是否是本轮投票、是否来⾃LOOKING状态的服务器。
  3. 处理投票
    · 优先检查ZXID。ZXID⽐较⼤的服务器优先作为Leader。
    · 如果ZXID相同,那么就⽐较myid。myid较⼤的服务器作为Leader服务器。
  4. 统计投票
    每次投票后,服务器都会统计所有投票,判断是否已经有过半的机器接收到相同的投票信息。对于Server1和Server2服务器来说,都统计出集群中已经有两台机器接受了(2,0)这个投票信息。这⾥我们需要对“过半”的概念做⼀个简单的介绍。所谓“过半”就是指⼤于集群机器数量的⼀半,即⼤于或等于(n/2+1)。对于这⾥由3台机器构成的集群,⼤于等于2台即为达到“过半”要求。
  5. 改变服务器状态
    ⼀旦确定了 Leader,每个服务器就会更新⾃⼰的状态:如果是 Follower,那么就变更为FOLLOWING,如果是Leader,那么就变更为LEADING。

4.2 服务器运⾏时期的Leader选举

  1. 变更状态
    Leader挂后,余下的⾮Observer服务器都会将⾃⼰的服务器状态变更为LOOKING,然后开始进⼊
    Leader选举过程。
  2. 每个Server会发出⼀个投票
    在运⾏期间,每个服务器上的ZXID可能不同,此时假定Server1的ZXID为123,Server3的ZXID为122;在第⼀轮投票中,Server1和Server3都会投⾃⼰,产⽣投票(1, 123),(3, 122),然后各⾃将投票发送给集群中所有机器。
  3. 余下步骤跟启动时期一致

总结

以上就是对zookeeper应用和进阶的一些内容,感兴趣的同学也可以到官网https://github.com/apache/zookeeper/tree/release-3.5.4 下载源码进行查看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值