分布式协调工具-Zookeeper原理使用学习

分布式协调

协调什么?

在分布式系统中,各个节点只能知道自身的事务执行情况,其他节点信息无法知晓.所以当一个事物需要跨节点执行时,为了保证这个事务的ACID,就需要一个协调者去协调各个节点的行为,觉最终决定各个节点的事务是否提交

怎么协调?

先学算法

一 2PC协议

算法前提
  1. 分布式系统中,存在一个协调者,剩余作为参与者,各个节点相互之间可以进行网络通信
  2. 所有节点都采用预写式日志,并且保存在可靠的储存设备上,即节点损坏也不会导致日志消失
  3. 所有的节点不会永久性损坏,即可恢复状态
基本算法
  • 第一阶段(提交请求阶段):
    1. 协调者向所有参与者节点询问是否可以执行提交操作,并等待参与者响应
    2. 参与者将事务执行至协调者询问处,并将Undo信息和Redo信息写入日志
    3. 参与者响应协调者发起的询问.即各个参与者节点事务执行成功则响应同意,不成功则中止
  • 第二阶段(提交执行阶段):
    • 成功:即所有参与者响应同意时
      1. 协调者向所有参与者节点发出正式提交请求
      2. 参与者正式完成操作,并释放所有资源
      3. 参与者执行完成后给协调者执行完成反馈
      4. 协调者收到所有参与者节点反馈后完成事务操作
    • 失败:任一参与者节点反馈中止或者第一节点无法获取所有参与者反馈时
      1. 协调者向所有参与者节点发送回滚操作请求
      2. 参与者利用Undo日志执行回滚操作,并释放资源
      3. 参与者向协调者反馈回滚完成
      4. 协调者收到所有参与者反馈后,取消事务
算法示意图
协调者                                        参与者
                    1.query to commit
                ---------------------->
                    2.vote yes/no           prepare*/abort*
                <----------------------
commit*/abort*          3.commit/rollback
                ---------------------->
                    4.acknowledgment        commit*/abort*
                <----------------------
end
算法缺陷
  1. 同步阻塞:所有节点都是事务阻塞型的
  2. 单点故障:协调者出现问题会使得参与者一直处于阻塞状态
  3. 数据不一致:二阶段时如果部分节点因网络等故障未接受到commit通知,则会造成整个分布式系统的数据不一致
  4. 二阶段设计缺陷: 极端情况下协调者发送commit通知后宕机,唯一接收到消息的参与者在接收后也发生宕机,那此时这条事务状态就无法被确认

二 3PC协议

相比2PC协议,引入超时机制,并将第一阶段再次拆分

基本算法
  • CanCommit阶段:协调者想参与者发送询问是否可以提交
  • PreCommit阶段
    • 执行提交:在上一阶段接收到yes反馈,就会进行事务预提交
      1. 发送预提交请求:协调者向参与者发送预提交请求,并进入准备阶段
      2. 事务预提交:参与者收到请求后,执行事务操作,并将Undo和Redo信息记录日志保存
      3. 响应反馈:参与者成功执行事务后,向协调者返回ACK码,等待最终指令
    • 中断事务:在上一阶段收到no反馈或者等待超时
      1. 发送中断请求:协调者向所有参与者发送abort请求
      2. 中断事务:参与者收到请求后执行事务中断操作
  • DoCommit阶段
    • 执行提交

      1. 协调者接收到参与者ACK响应码后,进入提交状态,并向所有参与者发送doCommit请求
      2. 事务提交:参与者收到doCommit请求后正式执行事务提交,并释放资源
      3. 响应反馈:事务执行完成后向协调者反馈ACK码
      4. 完成事务:协调者在收到所有参与者ACK响应后,完成事务
    • 中断事务:协调者没有收到参与者第二阶段的ACK响应码,执行中断事务操作

算法示意图
status  协调者                         参与者   status
投票阶段            canCommit?                  
            -------------------------->     
                    yes                       不确定
            <-------------------------- phase1
可提交             preCommit                   
            -------------------------->     预提交
                        ACK
            <-------------------------- phase2
                    doCommit
最终提交     -------------------------->     执行提交
                    ACK
end         <-------------------------- phase3
优缺点
  • 优点:降低阻塞范围
  • 缺点:在网络无法通信时依旧会产生数据不一致问题
区别
区别2PC3PC
阶段提交请求阶段及执行提交阶段只有协调者有超时判断;将提交请求阶段分成canCommit和preCommit两个阶段
超时只有协调者有超时判断参与者和协调者都有超时判断

三 paxos算法

  • 介绍:一种基于消息传递并且具有高度容错特性的一致性算法.一般分布式节点通信存在两种模型:共享内存和消息传递,所以不可避免的会产生进程杀死,消息丢失,重复,延迟等问题,paxos算法就是在以上情况下仍能保持一致性的协议
  • 算法过程
    牛逼吹了一大堆,我不知道过程是啥,哈哈哈哈哈哈,也不是我没看,是我没看懂,所以就不误导了

再学工具

zookeeper

简单认识

ZK是一个典型的分布式一致性解决方案,分布式应用程序可以借助它实现数据发布/订阅,负载均衡,命名服务,分布式协调/通知,集群管理,master选举,分布式锁和分布式队列等

基本概念

  1. 集群角色
    • leader:是整个集群工作机制中的核心,其主要工作有:
      1. 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。
      2. 集群内部各服务器的调度者。
    • follower:是zookeeper集群状态的跟随者,其主要工作是:
      1. 处理客户端的非事务请求,转发事务请求给leader服务器。
      2. 参与事务请求proposal的投票
      3. 参与leader选举投票
    • observer:和follower唯一的区别在于,observer服务器只提供非事务服务,不参与任何形式的投票,包括事务请求proposal的投票和leader选举投票。通常在不影响集群事务处理能力的前提下提升集群的非事务处理能力。
  2. 会话
    指客户端会话,一个客户端连接是指客户端和服务端之间的一个TCP长连接,Zookeeper对外的服务端口默认为2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够心跳检测与服务器保持有效的会话,也能够向Zookeeper服务器发送请求并接受响应,同时还能够通过该连接接受来自服务器的Watch事件通知。
  3. 数据节点
    • 机器节点:指构成集群的机器
    • 数据节点:指数据模型中的数据单元,Znode.
      • 持久节点:指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在Zookeeper上
      • 临时节点:生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除
      • 有序属性:SEQUENTIAL,一旦节点被标记上这个属性,那么在这个节点被创建的时候,Zookeeper会自动在其节点后面追加一个整形数字,其是由父节点维护的自增数字。
  4. 版本:对于每个ZNode,Zookeeper都会为其维护一个叫作Stat的数据结构,Stat记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本)、cversion(当前ZNode子节点的版本)、aversion(当前ZNode的ACL版本)。
  5. Watcher:Zookeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,Zookeeper服务端会将事件通知到感兴趣的客户端。
  6. ACL:Zookeeper采用ACL(Access Control Lists)策略来进行权限控制,其定义了如下五种权限::
    • CREATE:创建子节点的权限。
    • READ:获取节点数据和子节点列表的权限。
    • WRITE:更新节点数据的权限。
    • DELETE:删除子节点的权限。
    • ADMIN:设置节点ACL的权限。

zookeeper可以保证如下分布式一致性特性

  1. 顺序一致性:从同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到Zookeeper中;
  2. 原子性:所有事务的请求结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么在整个集群中所有机器上都成功应用了某一个事务,要么都没有应用,没有中间状态;
  3. 单一视图:无论客户端连接的是哪个Zookeeper服务器,其看到的服务端数据模型都是一致的。
  4. 可靠性:一旦服务端成功应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来,除非有另一个事务又对其进行了变更。
  5. 实时性:Zookeeper仅仅保证在一定的时间内,客户端最终一定能够从服务端上读到最终的数据状态。

zookeeper的四个设计目标

  1. 简单的数据模型:能够通过一个共享的、树型结构的名字空间来进行相互协调。

  2. 可以构建集群:Zookeeper使得分布式程序能够通过一个共享的树形结构的名字空间来进行相互协调,即Zookeeper服务器内存中的数据模型由一系列被称为ZNode的数据节点组成,Zookeeper将全量的数据存储在内存中,以此来提高服务器吞吐、减少延迟的目的。

  3. 顺序访问:对于来自客户端的每个更新请求,Zookeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序。

  4. 高性能:Zookeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用于以读操作为主的应用场景。

ZAB

为Zookeeper专门设计的一种支持崩溃恢复的原子广播协议。

ZAB协议

所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为leader服务器,而余下的其他服务器则成为follower服务器。leader服务器负责将一个客户端事务请求转换成一个事务proposal,并将该proposal分发给集群中所有的follower服务器。之后leader服务器需要等待所有follower服务器的反馈,一旦超过半数的follower服务器进行了正确的反馈后,那么leader就会再次向所有的follower服务器分发commit消息,要求其将前一个proposal进行提交。
ZAB协议需要确保那些已经在leader服务器上提交的事务最终被所有服务器都提交。ZAB协议需要确保丢弃那些只在leader服务器上被提出的事务。如果让leader选举算法能够保证新选举出来的leader服务器拥有集群中所有机器最高编号(ZXID)的事务proposal,那么就可以保证这个新选举出来的leader一定具有所有已经提交的提案。

ZAB两种基本模式

  • 崩溃恢复:
    当整个服务框架启动过程中或Leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入恢复模式并选举产生新的Leader服务器。

    当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后,ZAB协议就会退出恢复模式,那么整个服务框架就可以进入消息广播模式。

    Leader选举算法不仅仅需要让Leader自身知道已经被选举为Leader,同时还需要让集群中的所有其他机器也能够快速地感知到选举产生的新的Leader服务器。

    当Leader服务器出现崩溃或者机器重启、集群中已经不存在过半的服务器与Leader服务器保持正常通信时,那么在重新开始新的一轮的原子广播事务操作之前,所有进程首先会使用崩溃恢复协议来使彼此到达一致状态,于是整个ZAB流程就会从消息广播模式进入到崩溃恢复模式。

  • 消息广播:
    ZAB协议的消息广播过程使用原子广播协议,类似于一个二阶段提交过程,针对客户端的事务请求,Leader服务器会为其生成对应的事务Proposal,并将其发送给集群中其余所有的机器,然后再分别收集各自的选票,最后进行事务提交。

    整个消息广播协议是基于具有FIFO特性的TCP协议来进行网络通信的,因此能够很容易保证消息广播过程中消息接受与发送的顺序性。

    整个消息广播过程中,Leader服务器会为每个事务请求生成对应的Proposal来进行广播,并且在广播事务Proposal之前,Leader服务器会首先为这个事务Proposal分配一个全局单调递增的唯一ID,称之为事务ID(ZXID),由于ZAB协议需要保证每个消息严格的因果关系,因此必须将每个事务Proposal按照其ZXID的先后顺序来进行排序和处理。

    当一台同样遵守ZAB协议的服务器启动后加入到集群中,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么加入的服务器就会自觉地进入数据恢复模式:找到Leader所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。

ZAB协议基本特性

  • ZAB协议规定了如果一个事务Proposal在一台机器上被处理成功,那么应该在所有的机器上都被处理成功,哪怕机器出现故障崩溃。
  • ZAB协议需要确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交。
  • ZAB协议需要确保丢弃那些只在Leader服务器上被提出的事务
  • 如果在崩溃恢复过程中出现一个需要被丢弃的提议,那么在崩溃恢复结束后需要跳过该事务Proposal
  • 在崩溃恢复过程中需要处理的特殊情况,就决定了ZAB协议必须设计这样的

ZAB协议原理

ZAB主要包括消息广播和崩溃恢复两个过程,进一步可以分为三个阶段,分别是发现(Discovery)、同步(Synchronization)、广播(Broadcast)阶段。ZAB的每一个分布式进程会循环执行这三个阶段,称为主进程周期。

  • 发现,选举产生PL(prospective leader),PL收集Follower epoch(cepoch),根据Follower的反馈,PL产生newepoch(每次选举产生新Leader的同时产生新epoch)。
  • 同步,PL补齐相比Follower多数派缺失的状态、之后各Follower再补齐相比PL缺失的状态,PL和Follower完成状态同步后PL变为正式Leader(established leader)。
  • 广播,Leader处理客户端的写操作,并将状态变更广播至Follower,Follower多数派通过之后Leader发起将状态变更落地(deliver/commit)。

在正常运行过程中,ZAB协议会一直运行于阶段三来反复进行消息广播流程,如果出现崩溃或其他原因导致Leader缺失,那么此时ZAB协议会再次进入发现阶段,选举新的Leader。

每个进程都有可能处于如下三种状态之一

  • LOOKING:Leader选举阶段。
  • FOLLOWING:Follower服务器和Leader服务器保持同步状态。
  • LEADING:Leader服务器作为主进程领导状态。

一个Follower只能和一个Leader保持同步,Leader进程和所有与所有的Follower进程之间都通过心跳检测机制来感知彼此的情况。若Leader能够在超时时间内正常收到心跳检测,那么Follower就会一直与该Leader保持连接,而如果在指定时间内Leader无法从过半的Follower进程那里接收到心跳检测,或者TCP连接断开,那么Leader会放弃当前周期的领导,转换到LOOKING状态。

选举算法

不同场景的选举过程

一.服务器初始化时
  1. 每个Server发出投票
  2. 每个节点接受各个Server的投票
  3. 处理投票,处理规则如下:
    • 优先检查ZXID。ZXID比较大的服务器优先作为Leader。(这个很重要:是数据最新原则,保证数据的完整性)

    • 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。(集群的节点标识)

  4. 统计投票
  5. 改变各自服务器状态
二.服务器运行时
  1. 变更状态为looking
  2. 每个存活Server投票
  3. 接收投票
  4. 处理投票
  5. 统计投票
  6. 变更状态

选举算法详细分析

在3.4.0后的Zookeeper的版本只保留了TCP版本的FastLeaderElection选举算法

状态一:集群中已经存在Leader

此种情况一般都是某台机器启动得较晚,在其启动之前,集群已经在正常工作,对这种情况,该机器试图去选举Leader时,会被告知当前服务器的Leader信息,对于该机器而言,仅仅需要和Leader机器建立起连接,并进行状态同步即可。

状态二:集群中不存在Leader
  1. 第一次投票:
    无论哪种导致进行Leader选举,集群的所有机器都处于试图选举出一个Leader的状态,即LOOKING状态,LOOKING机器会向所有其他机器发送消息,该消息称为投票。投票中包含了SID(服务器的唯一标识)和ZXID(事务ID),(SID, ZXID)形式来标识一次投票信息。

  2. 变更投票:
    每台机器发出投票后,也会收到其他机器的投票,每台机器会根据一定规则来处理收到的其他机器的投票,并以此来决定是否需要变更自己的投票,这个规则也是整个Leader选举算法的核心所在,其中术语描述如下:

    • vote_sid:接收到的投票中所推举Leader服务器的SID。
    • vote_zxid:接收到的投票中所推举Leader服务器的ZXID。
    • self_sid:当前服务器自己的SID。
    • self_zxid:当前服务器自己的ZXID。

    每次对收到的投票的处理,都是对(vote_sid, vote_zxid)和(self_sid, self_zxid)对比的过程。

    • 规则一:如果vote_zxid大于self_zxid,就认可当前收到的投票,并再次将该投票发送出去。
    • 规则二:如果vote_zxid小于self_zxid,那么坚持自己的投票,不做任何变更。
    • 规则三:如果vote_zxid等于self_zxid,那么就对比两者的SID,如果vote_sid大于self_sid,那么就认可当前收到的投票,并再次将该投票发送出去。
    • 规则四:如果vote_zxid等于self_zxid,并且vote_sid小于self_sid,那么坚持自己的投票,不做任何变更。
  3. 确认Leader:
    经过第二轮投票后,集群中的每台机器都会再次接收到其他机器的投票,然后开始统计投票,如果一台机器收到了超过半数的相同投票,那么这个投票对应的SID机器即为Leader。

    由上面规则可知,通常那台服务器上的数据越新(ZXID会越大),其成为Leader的可能性越大,也就越能够保证数据的恢复。如果ZXID相同,则SID越大机会越大。

选举算法实现细节

一.服务器状态
  • LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入Leader选举状态。

  • FOLLOWING:跟随者状态。表明当前服务器角色是Follower。

  • LEADING:领导者状态。表明当前服务器角色是Leader。

  • OBSERVING:观察者状态。表明当前服务器角色是Observer。

二.投票数据结构
  • id:被推举的Leader的SID。

  • zxid:被推举的Leader事务ID。ZXID是一个64位的数字,其中低32位可以看做是一个简单的单调递增的计数器,针对客户端的每一个事务请求,Leader服务器在产生一个新的事务Proposal时,都会对该计数器进行加1操作;而高32位则代表了Leader周期epoch的编号,每当选举产生一个新的Leader时,就会从这个Leader上取出其本地日志中最大事务Proposal的ZXID,并解析出epoch['ɛpək]值,然后加1,之后以该编号作为新的epoch,低32位从0来开始生成新的ZXID,ZAB协议通过epoch号来区分Leader周期变化的策略,能够有效地避免不同的Leader服务器错误地使用不同的ZXID编号提出不一样的事务Proposal的异常情况。

  • electionEpoch:逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序列,每次进入新一轮的投票后,都会对该值进行加1操作。

  • peerEpoch:被推举的Leader的epoch。

  • state:当前服务器的状态。

心跳检测机制

Zookeeper常见问题

为什么zookeeper集群的数目,一般为奇数个?

  1. 容错

由于在增删改操作中需要半数以上服务器通过,来分析以下情况。

2台服务器,至少2台正常运行才行(2的半数为1,半数以上最少为2),正常运行1台服务器都不允许挂掉

3台服务器,至少2台正常运行才行(3的半数为1.5,半数以上最少为2),正常运行可以允许1台服务器挂掉

4台服务器,至少3台正常运行才行(4的半数为2,半数以上最少为3),正常运行可以允许1台服务器挂掉

5台服务器,至少3台正常运行才行(5的半数为2.5,半数以上最少为3),正常运行可以允许2台服务器挂掉

6台服务器,至少3台正常运行才行(6的半数为3,半数以上最少为4),正常运行可以允许2台服务器挂掉

通过以上可以发现,3台服务器和4台服务器都最多允许1台服务器挂掉,5台服务器和6台服务器都最多允许2台服务器挂掉

但是明显4台服务器成本高于3台服务器成本,6台服务器成本高于5服务器成本。这是由于半数以上投票通过决定的。

  1. 防脑裂

一个zookeeper集群中,可以有多个follower、observer服务器,但是必需只能有一个leader服务器。

如果leader服务器挂掉了,剩下的服务器集群会通过半数以上投票选出一个新的leader服务器。

集群互不通讯情况:

一个集群3台服务器,全部运行正常,但是其中1台裂开了,和另外2台无法通讯。3台机器里面2台正常运行过半票可以选出一个leader。

一个集群4台服务器,全部运行正常,但是其中2台裂开了,和另外2台无法通讯。4台机器里面2台正常工作没有过半票以上达到3,无法选出leader正常运行。

一个集群5台服务器,全部运行正常,但是其中2台裂开了,和另外3台无法通讯。5台机器里面3台正常运行过半票可以选出一个leader。

一个集群6台服务器,全部运行正常,但是其中3台裂开了,和另外3台无法通讯。6台机器里面3台正常工作没有过半票以上达到4,无法选出leader正常运行。

通可以上分析可以看出,为什么zookeeper集群数量总是单出现,主要原因还是在于第2点,防脑裂,对于第1点,无非是正常控制,但是不影响集群正常运行。但是出现第2种裂的情况,zookeeper集群就无法正常运行了。

ZooKeeper的脑裂的出现和解决方案

出现

在搭建hadoop的HA集群环境后,由于两个namenode的状态不一,当active的namenode由于网络等原因出现假死状态,standby接收不到active的心跳,因此判断active的namenode宕机,但实际上active并没有死亡。此时standby的namenode就会切换成active的状态,保证服务能够正常使用。若原来的namenode复活,此时在整个集群中就出现2个active状态的namenode,该状态成为脑裂。脑裂现象可能导致这2个namenode争抢资源,从节点不知道该连接哪一台namenode,导致节点的数据不统一,这在企业生产中是不可以容忍的。

解决方案
  1. 添加心跳线。
    原来两个namenode之间只有一条心跳线路,此时若断开,则接收不到心跳报告,判断对方已经死亡。此时若有2条心跳线路,一条断开,另一条仍然能够接收心跳报告,能保证集群服务正常运行。2条心跳线路同时断开的可能性比1条心跳线路断开的小得多。再有,心跳线路之间也可以HA(高可用),这两条心跳线路之间也可以互相检测,若一条断开,则另一条马上起作用。正常情况下,则不起作用,节约资源。
  2. 启用磁盘锁。
    由于两个active会争抢资源,导致从节点不知道该连接哪一台namenode,可以使用磁盘锁的形式,保证集群中只能有一台namenode获取磁盘锁,对外提供服务,避免数据错乱的情况发生。但是,也会存在一个问题,若该namenode节点宕机,则不能主动释放锁,那么其他的namenode就永远获取不了共享资源。因此,在HA上使用"智能锁"就成为了必要措施。"智能锁"是指active的namenode检测到了心跳线全部断开时才启动磁盘锁,正常情况下不上锁。保证了假死状态下,仍然只有一台namenode的节点提供服务。
  3. 设置仲裁机制
    脑裂导致的后果最主要的原因就是从节点不知道该连接哪一台namenode,此时如果有一方来决定谁留下,谁放弃就最好了。因此出现了仲裁机制,比如提供一个参考的IP地址,当出现脑裂现象时,双方接收不到对方的心跳机制,但是能同时ping参考IP,如果有一方ping不通,那么表示该节点网络已经出现问题,则该节点需要自行退出争抢资源的行列,或者更好的方法是直接强制重启,这样能更好的释放曾经占有的共享资源,将服务的提供功能让给功能更全面的namenode节点。

以上的3种方式可以同时使用,这样更能减少集群中脑裂情况的发生。但是还是不能保证完全不出现,如果仲裁机制中2台机器同时宕机,那么此时集群中没有namenode可以使用。此时需要运维人员人工的抢修,或者提供一台新的机器作为namenode,这个时间是不可避免的。希望未来能有更好的解决办法,能彻底杜绝这类情况的发生吧~

Zookeeper应用场景

典型应用场景

  • 数据发布/订阅
  • 负载均衡
  • 命名服务
  • 分布式协调/通知
  • 集群管理
  • Master选举
  • 分布式锁
  • 分布式队列
数据发布/订阅

数据发布/订阅系统,即配置中心。需要发布者将数据发布到Zookeeper的节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。发布/订阅一般有两种设计模式:推模式和拉模式,服务端主动将数据更新发送给所有订阅的客户端称为推模式;客户端主动请求获取最新数据称为拉模式,Zookeeper采用了推拉相结合的模式,客户端向服务端注册自己需要关注的节点,一旦该节点数据发生变更,那么服务端就会向相应的客户端推送Watcher事件通知,客户端接收到此通知后,主动到服务端获取最新的数据。

若将配置信息存放到Zookeeper上进行集中管理,在通常情况下,应用在启动时会主动到Zookeeper服务端上进行一次配置信息的获取,同时,在指定节点上注册一个Watcher监听,这样在配置信息发生变更,服务端都会实时通知所有订阅的客户端,从而达到实时获取最新配置的目的。

负载均衡

负载均衡是一种相当常见的计算机网络技术,用来对多个计算机、网络连接、CPU、磁盘驱动或其他资源进行分配负载,以达到优化资源使用、最大化吞吐率、最小化响应时间和避免过载的目的。

使用Zookeeper实现动态DNS服务

  • 域名配置,首先在Zookeeper上创建一个节点来进行域名配置,如DDNS/app1/server.app1.company1.com。

  • 域名解析,应用首先从域名节点中获取IP地址和端口的配置,进行自行解析。同时,应用程序还会在域名节点上注册一个数据变更Watcher监听,以便及时收到域名变更的通知。

  • 域名变更,若发生IP或端口号变更,此时需要进行域名变更操作,此时,只需要对指定的域名节点进行更新操作,Zookeeper就会向订阅的客户端发送这个事件通知,客户端之后就再次进行域名配置的获取。

命名服务

命名服务是分步实现系统中较为常见的一类场景,分布式系统中,被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等,通过命名服务,客户端可以根据指定名字来获取资源的实体、服务地址和提供者的信息。Zookeeper也可帮助应用系统通过资源引用的方式来实现对资源的定位和使用,广义上的命名服务的资源定位都不是真正意义上的实体资源,在分布式环境中,上层应用仅仅需要一个全局唯一的名字。Zookeeper可以实现一套分布式全局唯一ID的分配机制。

分布式协调/通知

Zookeeper中特有的Watcher注册于异步通知机制,能够很好地实现分布式环境下不同机器,甚至不同系统之间的协调与通知,从而实现对数据变更的实时处理。通常的做法是不同的客户端都对Zookeeper上的同一个数据节点进行Watcher注册,监听数据节点的变化(包括节点本身和子节点),若数据节点发生变化,那么所有订阅的客户端都能够接收到相应的Watcher通知,并作出相应处理。

MySQL数据复制总线是一个实时的数据复制框架,用于在不同的MySQL数据库实例之间进行异步数据复制和数据变化通知,整个系统由MySQL数据库集群、消息队列系统、任务管理监控平台、Zookeeper集群等组件共同构成的一个包含生产者、复制管道、数据消费等部分的数据总线系统。

Zookeeper主要负责进行分布式协调工作,在具体的实现上,根据功能将数据复制组件划分为三个模块:Core(实现数据复制核心逻辑,将数据复制封装成管道,并抽象出生产者和消费者概念)、Server(启动和停止复制任务)、Monitor(监控任务的运行状态,若数据复制期间发生异常或出现故障则进行告警)

在绝大多数分布式系统中,系统机器间的通信无外乎心跳检测、工作进度汇报和系统调度。

  1. 心跳检测,不同机器间需要检测到彼此是否在正常运行,可以使用Zookeeper实现机器间的心跳检测,基于其临时节点特性(临时节点的生存周期是客户端会话,客户端若当即后,其临时节点自然不再存在),可以让不同机器都在Zookeeper的一个指定节点下创建临时子节点,不同的机器之间可以根据这个临时子节点来判断对应的客户端机器是否存活。通过Zookeeper可以大大减少系统耦合。

  2. 工作进度汇报,通常任务被分发到不同机器后,需要实时地将自己的任务执行进度汇报给分发系统,可以在Zookeeper上选择一个节点,每个任务客户端都在这个节点下面创建临时子节点,这样不仅可以判断机器是否存活,同时各个机器可以将自己的任务执行进度写到该临时节点中去,以便中心系统能够实时获取任务的执行进度。

  3. 系统调度,Zookeeper能够实现如下系统调度模式:分布式系统由控制台和一些客户端系统两部分构成,控制台的职责就是需要将一些指令信息发送给所有的客户端,以控制他们进行相应的业务逻辑,后台管理人员在控制台上做一些操作,实际上就是修改Zookeeper上某些节点的数据,Zookeeper可以把数据变更以时间通知的形式发送给订阅客户端。

集群管理

Zookeeper的两大特性:

  • 客户端如果对Zookeeper的数据节点注册Watcher监听,那么当该数据及该单内容或是其子节点列表发生变更时,Zookeeper服务器就会向订阅的客户端发送变更通知。
  • 对在Zookeeper上创建的临时节点,一旦客户端与服务器之间的会话失效,那么临时节点也会被自动删除。
  • 变化的日志源机器
  • 变化的收集器机器
Master选举

在分布式系统中,Master往往用来协调集群中其他系统单元,具有对分布式系统状态变更的决定权,如在读写分离的应用场景中,客户端的写请求往往是由Master来处理,或者其常常处理一些复杂的逻辑并将处理结果同步给其他系统单元。利用Zookeeper的强一致性,能够很好地保证在分布式高并发情况下节点的创建一定能够保证全局唯一性,即Zookeeper将会保证客户端无法重复创建一个已经存在的数据节点。

首先创建/master_election/2016-11-12节点,客户端集群每天会定时往该节点下创建临时节点,如/master_election/2016-11-12/binding,这个过程中,只有一个客户端能够成功创建,此时其变成master,其他节点都会在节点/master_election/2016-11-12上注册一个子节点变更的Watcher,用于监控当前的Master机器是否存活,一旦发现当前Master挂了,其余客户端将会重新进行Master选举。

分布式锁

分布式锁用于控制分布式系统之间同步访问共享资源的一种方式,可以保证不同系统访问一个或一组资源时的一致性,主要分为排它锁和共享锁。

排它锁又称为写锁或独占锁,若事务T1对数据对象O1加上了排它锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能再对这个数据对象进行任何类型的操作,直到T1释放了排它锁。

  1. 获取锁,在需要获取排它锁时,所有客户端通过调用接口,在/exclusive_lock节点下创建临时子节点/exclusive_lock/lock。Zookeeper可以保证只有一个客户端能够创建成功,没有成功的客户端需要注册/exclusive_lock节点监听。

  2. 释放锁,当获取锁的客户端宕机或者正常完成业务逻辑都会导致临时节点的删除,此时,所有在/exclusive_lock节点上注册监听的客户端都会收到通知,可以重新发起分布式锁获取。

共享锁又称为读锁,若事务T1对数据对象O1加上共享锁,那么当前事务只能对O1进行读取操作,其他事务也只能对这个数据对象加共享锁,直到该数据对象上的所有共享锁都被释放。

  1. 获取锁,在需要获取共享锁时,所有客户端都会到/shared_lock下面创建一个临时顺序节点,如果是读请求,那么就创建例如/shared_lock/host1-R-00000001的节点,如果是写请求,那么就创建例如/shared_lock/host2-W-00000002的节点。

  2. 判断读写顺序,不同事务可以同时对一个数据对象进行读写操作,而更新操作必须在当前没有任何事务进行读写情况下进行,通过Zookeeper来确定分布式读写顺序,大致分为四步。

    1. 创建完节点后,获取/shared_lock节点下所有子节点,并对该节点变更注册监听。

    2. 确定自己的节点序号在所有子节点中的顺序。

    3. 对于读请求:若没有比自己序号小的子节点或所有比自己序号小的子节点都是读请求,那么表明自己已经成功获取到共享锁,同时开始执行读取逻辑,若有写请求,则需要等待。对于写请求:若自己不是序号最小的子节点,那么需要等待。

    4. 接收到Watcher通知后,重复步骤1。

  3. 释放锁,其释放锁的流程与独占锁一致。

host1客户端在移除自己的共享锁后,Zookeeper发送了子节点更变Watcher通知给所有机器,然而除了给host2产生影响外,对其他机器没有任何作用。大量的Watcher通知和子节点列表获取两个操作会重复运行,这样会造成系能鞥影响和网络开销,更为严重的是,如果同一时间有多个节点对应的客户端完成事务或事务中断引起节点小时,Zookeeper服务器就会在短时间内向其他所有客户端发送大量的事件通知,这就是所谓的羊群效应。

可以有如下改动来避免羊群效应。

  1. 客户端调用create接口常见类似于/shared_lock/[Hostname]-请求类型-序号的临时顺序节点。

  2. 客户端调用getChildren接口获取所有已经创建的子节点列表(不注册任何Watcher)。

  3. 如果无法获取共享锁,就调用exist接口来对比自己小的节点注册Watcher。对于读请求:向比自己序号小的最后一个写请求节点注册Watcher监听。对于写请求:向比自己序号小的最后一个节点注册Watcher监听。

  4. 等待Watcher通知,继续进入步骤2。

此方案改动主要在于:每个锁竞争者,只需要关注/shared_lock节点下序号比自己小的那个节点是否存在即可。

分布式队列

分布式队列可以简单分为先入先出队列模型和等待队列元素聚集后统一安排处理执行的Barrier模型。

  1. FIFO先入先出,先进入队列的请求操作先完成后,才会开始处理后面的请求。FIFO队列就类似于全写的共享模型,所有客户端都会到/queue_fifo这个节点下创建一个临时节点,如/queue_fifo/host1-00000001。

    创建完节点后,按照如下步骤执行。

    1. 通过调用getChildren接口来获取/queue_fifo节点的所有子节点,即获取队列中所有的元素。

    2. 确定自己的节点序号在所有子节点中的顺序。

    3. 如果自己的序号不是最小,那么需要等待,同时向比自己序号小的最后一个节点注册Watcher监听。

    4. 接收到Watcher通知后,重复步骤1。

  2. Barrier分布式屏障,最终的合并计算需要基于很多并行计算的子结果来进行,开始时,/queue_barrier节点已经默认存在,并且将结点数据内容赋值为数字n来代表Barrier值,之后,所有客户端都会到/queue_barrier节点下创建一个临时节点,例如/queue_barrier/host1。

    创建完节点后,按照如下步骤执行。

    1. 通过调用getData接口获取/queue_barrier节点的数据内容,如10。

    2. 通过调用getChildren接口获取/queue_barrier节点下的所有子节点,同时注册对子节点变更的Watcher监听。

    3. 统计子节点的个数。

    4. 如果子节点个数还不足10个,那么需要等待。

    5. 接受到Wacher通知后,重复步骤3。

参考

https://blog.csdn.net/paincupid/article/details/78058087
https://www.cnblogs.com/sweet6/p/10574574.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值