分布式协调服务Zookeeper扫盲篇

                                           作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

 

  身为运维工程师对kubernetes(k8s)可能比较熟,那么etcd(go语言实现)分布式协调服务应该也有一定的了解吧。在大数据领域中,也有一个分布式协调工具基本上可以和zookeeper分庭抗礼,他就是我们今天要说的zookeeper。

  在分布式系统中,服务(或组件)之间的协调是非常重要的,它构成了分布式系统的基础。分布式系统中的leader选举,分布式锁,分布式队列等,均需要通过协调服务(Coordination Service)实现。然而,由于分布式环境的复杂性,尤其是在网络故障,死锁,竞争等已变为常见现象的情况下,实现一个鲁棒的协调服务是极为困难的事情。为了实现一个通用的分布式文件协调服务,避免每个分布式系统从头实现造成不必要的工作冗余,Hadoop生态系统提供了Zookeeper。

  Zookeeper通过引入类似于文件系统的层级命名空间,并在此基础上提供了一套简单的易用的原语,能够帮助用户轻易地实现前面提到leader选举,分布式锁,分布式队列等功能。zookeeper已被大量开源系统采用,包括HDFS(leader选举问题),YARN(leader选举问题),HBase(leader选举和分布式锁等)等。

 

一.Zookeeper概述

1>.zookeeper诞生背景

    zookeeper是一款分布式协同管理框架,是Google Ghubby的开源实现,主要用于解决分布式应用中经常会遇到的一些问题。
zookeeper自身拥有很高度的可靠性,可扩展性和容错性,能提供统一命名服务,分布式锁,分布式队列,选举,配置同步,心跳检查等功能。有了zookeeper的帮助,开发实现一个分布式系统就会显得容易很多。像HBase,Kafka,SorlColoud等众多知名框架都是使用zookeeper实现分布式协同管理的。
zookeeper的逻辑架构也是一个典型的主从架构,它和众多Server服务中通过选举产生一个主控节点。

2>.zookeeper核心特性

    zookeeper的目标是基于自身去构建更为复杂的分布式应用场景,例如分布式数据库,分布式消息系统和分布式搜索引擎这类实时性要求很高的系统,所以它自身被设计的非常快速,非常简单。同时,为了能够支撑分布式场景下食物的一致性,zookeeper提供了一些核心特性:

顺序一致性:
  客户端发送的更新请求将按照发送的顺序进行执行。

原子性:
    更新操作只会有更新成功或更新失败两种状态,不会存在其他状态。

单一系统视图:
  客户端链接到任意的服务器都将看到相同的数据视图。

可靠性:
  一旦一个更新动作被执行,所有的服务都见给执行这个更新动作。

及时性:
  客户端看到的视图在一定时间内保证是最新的。
                  

3>.原子消息广播协议

    ZAB(Zookeeper Atomic Broadcast)是一种数据分布式一致性算法,是Paxos算法的简化版本。可以说ZAB是zookeeper立足的根本。在说ZAB协议之前,我们先简单回顾一下分布式一致性的发展历史。

一.两阶段提交协议
  在单机数据库时代,我们没有分布式数据一致性的问题的困扰。通过数据库事物我们就能轻松达成ACID的特性从而保证数据的一致性。
  但是当我们进入分布式时代对时候,单机事务就显得力不从心了。在分布式服务的场景下,我们会有多个物理上独立的数据库分布在不同的服务器。从单机的视角来看,每个独立的数据库内部都能通过单机事务保证数据的一致性,但是如果一个操作同时涉及到多个独立数据库的时候,就会出现数据不一致的情况。
  于是人们设计了一个名叫两阶段提交(Two-PhaseConmmit,2PC)的协议来解决这个问题。简单的说两阶段提交就是在客户端和多个数据库之间增加了一个事务协调者,同时将事物的提交分为准备和提交两个阶段。
1>.准备阶段
  事务协调者给每个数据库发送prepare消息,每个数据库要在本地执行事务,写本地的redo和undo日志,但不提交事务。大致流程如下:
    (1):事务协调者节点向所有数据库询问是否可以执行提交操作,并开始等待各参与者节点的响应。
    (2):数据库节点执行询问发起为止的所有事务操作,并将undo信息和redo信息写入日志。
    (3):数据库节点响应事务协调者节点发起的询问。如果数据库节点的事务操作实际执行成功,则返回一个“同意”消息;如果数据库节点的事务操作实际执行成功,则返回一个“同意”消息;如果数据库节点的事务操作执行失败,则返回一个“中指”的消息。
2>.提交阶段
  如果事务协调者收到了数据库的中止消息或者等待超时,则直接给每个数据库发送rollback消息,要求进行回滚操作;如果事务协调者收到了数据库的同意消息,则发送commit消息,要求进行提交操作。执行完毕之后,释放所有事务处理过程中使用的资源。大致流程如下:
    (1):事务协调者节点向所有数据库发起“commit/rollback”请求,并开始等待各参与者节点的响应。
    (2):数据库节点执行“commit/rollback”动作并释放事务占用的资源,之后向事务协调者发送完成消息。
    (3):事务协调者收到所有数据库的反馈消息后完成或取消事务。
  两阶段提交看似解决了分布式数据一致性的问题,其实这个设计存在一个明显的问题:
    (1):阻塞执行,速度慢
          从刚才描述可以看出,协调者和数据库的一系列提交或回顾动作都是阻塞执行的,这必然导致整个分布式事务运行效率缓慢。
    (2):单点问题
          两阶段提交的核心枢纽是事务协调者节点,如果这个节点失效了,那么整个事务机制也就瘫痪了。同时由于协调这失效,回导致数据库的资源一直占有无法释放。
    (3):数据不一致
          试想一下,在提交阶段,当事务协调者向所有数据库发送commit请求之后,由于网络问题只是提供一部分数据库收到了请求信息并执行了commit动作,而另一部分数据库没有收到commit请求信息,多个数据库之间就会产生数据不一致的问题。

二.三阶段提交协议
  为了解决两阶段提交协议的缺陷,人们又提出了一个改进版本,这就是三阶段条协议(Three-PhaseCommit ,3PC)。将协调者和数据库都引入超时机制,以解除咋就协调者失效的时候,数据库回一直占有资源无法释放的问题,同时又将准备阶段拆分成了询问和准备两个阶段以增加容错概率。
1>.询问阶段
  事务协调者给每个数据库发送CanCommit请求,每个数据库如果可以提交就返回YES消息,否则就返回NO消息。大致流出如下:
    (1):事务协调者节点向所有数据库发起CanCommit请求,并开始等待各参与者节点的响应。
    (2):数据库节点收到CanCommit请求之后如果可以提交,则返回YES消息并进入准备阶段。否则返回NO消息。
2>.准备阶段
  事务协调者收到反馈后会有两种情况产生,如下所示。
  数据库返回的消息均为YES消息,则执行预执行。大致流程如下:
    (1):事务协调者节点向所有数据库发起PreCommit请求,并开始等待各参与者节点的响应。
    (2):数据库节点执行到询问发起为止的所有事物操作,并将undo信息和redo信息写入日志。执行成功后返回ACK应答,并进入等待。
  数据库返回的消息含有NO消息或者等待超时,则执行事务中断。大致流程如下:
    (1):事务协调者节点向所有的数据库发起abort中断请求。
    (2):数据库节点收到abort请求之后,执行事务中断。如果在超时之后还没有收到事务协调者的任何消息,也执行事务中断动作。
3>.提交阶段
  事务协调者收到反馈后会有两种情况产生。
  协调者收到所有ACK应答,则执行事务提交,大致流程如下:
    (1):事务协调者节点下个所有数据库发起DoCommit请求,并开始等待各参与者节点的响应。
    (2):数据库节点执行commit动作并释放事务占用的资源,之后向事务协议者发送ACK消息应答。
    (3):事务协调者收到所有数据库的反馈消息后完成事务。
  协调者没有收到所有ACK应答,则执行事务中断,大致流程如下:
    (1):事务协调者节点向所有数据库发起abort中断请求。
    (2):数据库节点收到abort请求之后执行事务回滚,并向事务协调者发送ACK消息应答。
    (3):事务协调者说到所有数据库反馈消息后完成事务中断。

  从三阶段提交的流出来看,已经解决了两阶段提交中的一些问题,但还是会出现数据不一致但问题。因为在进行第三阶段也就是提交阶段,如果数据在超时钱没有收到DoCommit或abort消息,那么它最终会执行commit动作。
  试想一下,如果在提交阶段事务协调者没有收到ACK应答,那么它会发送abort中断事务的请求。碰巧这个时候网络发生了抖动导致一部分数据库没有收到abort消息,那么收到消息的数据库会执行事务中断,而没有收到abort消息的数据库最终执行commit动作,这就导致数据不一致了。

三.Paxos协议
  为了完美解决分布式场景下数据一致性的问题,Paxos算法诞生了。Paxos算法原文十分难以理解,由于篇幅有限,这里主要是通过作者Leslie Lamport另一篇相对简单的论文《Paxos Mode Simple》来进行简单描述。具体的内容可以看Poxos Mode Simple论文的全文。
  假设有一组可以提出提案的进程集合,提案用【编号,值】的形式来进行描述,一致性算法需要保证以下几点:  
    (1):只有当一个提案被提出后才能会被选定。
    (2):这些进程集合中只有一个提案会被选定。
    (3):如果某个进程认为某个提案被选定了,那么这个提案确实是已经被选定了。
  该一致性算法分为三个角色,我们用proposer,acceptor和leader来表示。proposer可以批准提案,acceptor可以提交提案,而leader只能获取已经被批准的提案。
1>.阶段一
  (1):proposer选择一个提交编号n,然后向超过半数的acceptor发送编号n的proposer请求。
  (2):如果一个acceptor收到了一个编号为n的prepare请求,并且编号n大于这个acceptor之前已经响应的prepare请求的编号,那么它会将已经批准过的最大编号的提案作为响应发送给proposer,并且承诺不会在批准任何编号小于n的提案。
2>.阶段二
  (1):如果proposer收到了半数以上的acceptor对编号n的propare请求的回应,那么它会会发送一个针对[n,v]提案的accept请求答谢acceptor。这里的v的值就是收到响应的编号最大提案的值。
  (2):如果accept收到这个针对[n.v]提案的accept请求,只要该acceptor还没有对编号大于n提案进行响应,它就会通过这个提案。
  
  Paxos通过引入过半提交的概念解决了在两个阶段和三阶段提交中会出现的种种问题。正如Chubby的作者Mike Burrows所说的那样,这个世界上只有一种分布式一致性算法,那就是Paxos。

四.ZAB协议
  Paxos算法理论上虽然完美,但是实现起来太过复杂,因为它的目标是构建一个去中心化的,通过的分布式一致性算法。而且Paxos算法只在乎数据的一致性而不关心事务请求的顺序,这一点并不难满足Zookeeper的要求。
  因为ZOokeeper的命名空间是一个树形结构,对执行的顺序有严格的要求,于是zookeeper借助了了Paxos过半提交的思想将两阶段提交进行优化改造,ZAB就这样诞生了。ZAB协议中有三种角色:
    (1):Leader
         所有的写请求首先都会转发到Leader节点上,Leader节点上的数据变更会同步到集群的Follower节点上。
    (2):Follower
         负责同步Leader姐弟啊你的数据,并提供数据的查询能力。当Leader节点失效的时候有权利参与投票选举。
    (3):Observer
         同步Leader节点的数据,并提供数据的查询功能。没有投票选举,Observer的设计目的是提高集群的查询能力。

   和Paxos有所不同,ZAB并不是一个无中心话的架构,在任意时刻ZAB都保持有且仅有一个Leader节点,所有的更新事务都只能由这个Leader发起。并且当一个Leader失效以后,新的Leader只有在之前Leader的事务都被处理之后才能发起新的事务。通过这种机制,ZAB协议保证了全局的事务顺序。在更新阶段ZAB使用的就是一个优化过的两阶段提交,这里借助了Paxos的思想,只要过半的节点prepare成功,就会发起commit请求。
  如果想查看ZAB完整协议内容可以定于它的论文:《Zab:High-performance broadcast for primary-backup systems》,链接地址为:https://www.semanticscholar.org/paper/Zab%3A-High-performance-broadcast-for-primary-backup-Junqueira-Reed/b02c6b00bd5dbdbd951fddb00b906c82fa80f0b3
    ACID是Atomic(原子性),Consistency(一致性),Isolation(隔离性)和Durability(持久性)的英文缩写。

原子性:
    将一组操作视为一个原子操作,操作要么全部执行,要么全部不执行。

一致性:
    一个事务在执行之前和执行之后数据库都必须处在一个一致性的状态。

隔离性:
    不同事务之间是相互隔离的。

持久性:
    一但一个事务被提交,它对数据库中对数据改变应该是永久性对。
ACID知识扩展

相关文章:

  • 2021-06-13
  • 2022-12-23
  • 2022-02-22
  • 2021-04-12
  • 2022-01-05
  • 2021-04-27
  • 2021-06-30
猜你喜欢
  • 2021-05-02
  • 2022-01-22
  • 2021-08-22
  • 2021-08-08
  • 2021-05-17
  • 2021-12-11
相关资源
相似解决方案