《从Paxos到Zookeeper 分布式一致性原理与实践》---读后总结

第一章

系统由集中式到分布式进化。java

分布式问题

通讯异常:因为网络的不可靠,致使分布式系统各个节点之间通信伴随着网络不可用风险。mysql

网络分区:网络异常致使部分节点之间延时过大,最终致使只有部分节点之间能正常通讯,这种现象就是网络分区---俗称“脑裂”。算法

从ACID到CAP/BASE

单机系统很容易知足ACID,可是分布式系统对这些数据进行事务处理则面临很大挑战。sql

CAP

一致性(C):数据在多个副本之间可以保持一致的特性。数据库

可用性(A):服务一直处于可用的状态,对于用户的每一个操做总能在有限的时间内返回结果。设计模式

分区容错性(P):分布式系统在遇到任何网络分区故障的时候,仍然须要可以保证对外提供知足一致性和可用性的服务,除非是整个网络环境发生故障。bash

分区容错性是分布式系统的基本须要。服务器

BASE

BASE是 Basically Avaiable(基本可用)、Soft State(软状态)、Eventually consistent(最终一致性)的简称。是基于CAP理论演化来的,核心思想是即便没法作到强一致性,但每一个应用均可以根据自身的业务特色,采用适当的方式使系统达到最终一致性。网络

基本可用:系统在出现不可预知故障的时候,容许损失部分可用性(响应时间损失、功能上的损失等)。app

软状态:容许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的总体性,即容许系统在不一样节点的数据副本之间进行数据同步的过程存在延时。

最终一致性:指系统全部的数据副本,在通过一段时间的同步后,最终都能达到一个一致的状态。

第二章

由数据一致性问题,提出一致性协议与算法。阐述了两阶段提交协议和三阶段提交协议,重点讲述Paxos一致性算法。

两阶段提交协议

简单来说,两阶段提交将一个事务的处理过程分为投票和执行两个阶段,其核心是对每一个事务都采用先尝试后提交的处理方式。

事务提交与事务中断过程如图所示:

优势

原理简单,实现方便。

缺点

同步阻塞,单点问题,脑裂,太过保守。

三阶段提交协议

三阶段提交协议将二阶段提交协议的“提交事务请求”过程一分为二,造成了由Cancommit,Precommit,Docommit三个阶段。

过程以下:

优势

下降了参与者的阻塞范围,而且能在单点故障后继续达成一致。

缺点

若是参与者收到了preCommit消息后出现了网络分区,此时协调者所在的节点和参与者没法进行正常的网络通讯,在这种状况下,该参与者依然会进行事务的提交,必然会形成数据的不一致。

Paxos算法

是一种基于消息传递且具备高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一。

算法解析

参考了一篇文章,写的仍是挺容易理解的。

@Paxos算法解析

Paxos的主要的两个组件:

Proposer

提议发起者,处理客户端请求,将客户端的请求发送到集群中,以便决定这个值是否能够被批准。

Acceptor

提议批准者,负责处理接收到的提议,他们的回复就是一次投票。会存储一些状态来决定是否接收一个值。

规则以下:
1 一个Acceptor必须接受它收到的第一个提案。
2 一个提案被选定须要被半数以上的Acceptor接受。
3 若是某个value为v的提案被选定了,那么每一个编号更高的被选定提案的value必须也是v。

一个提案被选定要通过第一阶段(prepare),若是一半Acceptor赞成提案,Proposer才能第二阶段(Accept)。若是一半Acceptor赞成提案,提案才能被选定。

第一阶段(prepare)

1)提议者获取一个提案号(proposal number)n;

2)提议者向全部节点广播prepare(n)请求;

3)接收者接收消息此时要分几种状况处理:

a.若是接收者尚未收到任何提议,记录minProposal.并返回提议者OK,承认该提案。
b.若是接收者已有minProposal,比较n,和minProposal,若是n>minPno,表示有更新的提案,更新minProposal=n。
c.若是此时已经有一个认定值(accptedValue),返回提议者已肯定的(acceptedProposal,acceptedValue)。
d.若是接收者已有minProposal,比较n,和minProposal,若是n<minProposal.拒绝而且返回minProposal。

4)提议者根据请求返回的响应作处理。只有收到超过一半接收者响应才能发起第二阶段请求。一样也分为如下几种状况:

a.超过一半接收者返回已肯定的提案(acceptedProposal,acceptedValue),表示有承认的提议,保存最高acceptedProposal编号的acceptedValue到本地。
b.超过一半接收者赞成提议者的提案。
c.未获得一半接收者赞成提案,这种请求跳转1,对n进行累加,从新prepare。

第二阶段(Accept)

5)提议者广播accept(n,value)到全部提议者节点;
6) 接收者比较n和minProposal,若是n>=minProposal,则acceptedProposal=minProposal=n,acceptedValue=value,本地持久化后,返回; 不然,拒绝而且返回minProposal。
7) 提议者接收到过半数请求后,若是发现有返回值>n,表示有更新的提议,跳转1(从新发起提议);不然value达成一致,提案被选定

如下经过实例说明:

示例一:

P1发送提议【1,a】至A1,A2 , A1,A2由于此时是第一个提议,因此接收提议,返回OK。

由于P1收到一半Acceptor的响应,因此发送第二阶段请求。

A1,A2 比较本地的minProposal 发现n=minProposal 选定【1,a】并返回给P1,P1发现有一半接受者赞成提案,选定【1,a】。

P2发送提案【2,b】给A1,A2 由于此时有选定值【1,a】,返回【1,a】,P2发现有一半接收者返回选定值,更新本地提案。

示例二:

P1发送提议【1,a】至A1,A2 , A1,A2由于此时是第一个提议,因此接收提议,返回OK。

P2发送提议【2,b】至A1,A2 , 由于【2,b】大, A1,A2更新本地minProposal 返回OK。

由于一半接收者赞成P1的提案,因此发送accept请求【1,a】,A1,A2比较本地minProposal 发现本地minProposal为【2,b】。

因此拒绝提案,并返回【2,b】给P1,表示有更新提案。

P1更新提案【3,a】继续第一阶段prepare请求。

可是,若是此时P2在进行accept请求【2,b】发现又有更新请求。 如此反复就会出现活锁的状况,解决的方法就是提出主Proposer,并规定只有主Proposer能够提出议案

这样,只要主Proposer和过半的Acceptor可以正常进行网络通讯,但凡主Proposer提出一个编号更高的提案,该提案终将会被批准。整套Paxos算法流程就能保持活性。

第三章

本章主要经过对Google Chubby 和 Hypertable这两款经典的分布式产品中的Paxos算法应用的介绍,阐述了Paxos算法在实际工业实践的应用。在此,不过多赘述。

第四章

本章首先对Zookeeper进行一个总体上的介绍,包括设计目标、由来及基本概念,重点介绍Zookeeper中的ZAB一致性协议。

Zookeeper是什么

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

Zookeeper能够保证以下分布式一致性特性:

顺序一致性

从同一个客户端发起的事务请求,最终将会严格按照发起的顺序被应用到Zookeeper中。

原子性

全部事务请求的处理结果在整个集群中全部机器上的应用状况是一致的。

单一视图

不管客户端连接的是那个Zookeeper服务器,看到的服务端数据模型都是一致的。

可靠性

一旦服务端成功的应用了一个事务,并完成对客户端的响应,那么该事务所引发的服务端状态变动将会被一直保留下来,除非有另外一个事务又对其进行了变动。

实时性

Zookeeper仅仅保证在必定的时间段内,客户端最终必定可以从服务端读取到最新的数据状态。

ZAB协议

不少读者认为Zookeeper是Paxos算法的一个实现,事实上,并无彻底采用Paxos算法,而是使用了ZAB协议。ZAB协议是为Zookeeper设计的一种支持崩溃恢复的原子广播协议。

ZAB协议主要包含两种基本模式:崩溃恢复,消息广播。

消息广播

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

以下图所示:

但与二阶段提交不一样的是,ZAB在二阶段提交过程当中,移除了中断逻辑,全部的Follower要么正常反馈Leader提出的Proposal,要么抛弃Leader。同时,意味着咱们能够在过半的Follower服务器已经反馈ACK以后就能够提交事务,不须要等待全部的Follower都反馈。整个消息广播协议是基于具备FIFO特性的TCP协议进行网络通信的,所以保证了消息的有序性。

可是,这样的二阶段提交不能保证Leader崩溃后数据的一致性,所以还须要崩溃恢复模式。

崩溃恢复

在ZAB协议中,为了保证程序的正确运行,整个恢复过程结束后须要选举出一个新的Leader服务器。所以,ZAB协议须要一个高效且可靠的Leader选举算法,从而确保能快速的选举出新的Leader。同时,Leader选举算法不只仅须要让Leader本身知道其自身被选举为Leader,还须要让集群中的其余机器知道。

数据同步

全部正常运行的服务器,要么成为Leader,要么成为Follower并和Leader保持同步。 Leader服务器须要确保全部的Follower服务器可以接收到每一条事务Proposal,而且能 够正确地将全部已经提交了的事务Proposal应用到内存数据库中去。具体的,Leader服 务器会为每个Follower服务器都准备一个队列,并将那些没有被各Follower服务器同 步的事务以Proposal消息的形式逐个发送给Follower服务器,并在每个Proposal消息 后面紧接着再发送一个Commit消息,以表示该事务已经被提交。等到Follower服务器 将全部其还没有冋步的事务Proposal都从Leader服务器上同步过来并成功应用到本地数 据库中后,Leader服务器就会将该Follower服务器加入到真正的可用Follower列表中, 并开始以后的其余流程。

ZAB和Paxos算法的联系和区别

联系:

  • 二者都存在一个相似与Leader进程的角色,由其负责协调多个Follower进程的运行。
  • Leader进行都会等待超半数的Follower作出正确反馈后才提交事务。
  • 在ZAB协议中,每一个Proposal中都包含一个epoch值用来表明当前leader的周期,在Paxos中,一样有这样的标识,名字改成Ballot。

区别:

二者设计目标不太同样,ZAB用于构建一个分布式数据主备系统,而Paxos用于构建一个分布式一致性状态机系统。

第五章

本章主要介绍如何使用Zookeeper,包括部署与运行,以及Java客户端的调用,都是一些比较基础的使用方法,在此不过多赘述。

第六章

本章主要介绍Zookeeper的典型应用场景以及实现。

数据发布、订阅

数据发布、订阅系统,即所谓的配置中心,顾名思义就是发布者将数据发布到Zookeeper的一个或一系列节点上,供订阅者进行数据订阅,从而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。

发布、订阅系统通常有两种设计模式,分别是推(PUSH)模式和拉(PULL)模式。在推模式中,服务器主动将数据更新发送给全部订阅的客户端;而拉模式则是由客户端主动发起请求来获取最新数据,一般客户端都采用定时进行轮训拉取的方式。

Zookeeper采用的是推拉相结合的方式:客户端向服务端注册本身须要关注的节点,一旦该节点的数据发生变动,那么服务端就会向相应的客户端发送Wather事件通知,客户端接收到这个消息通知以后,须要主动到服务端获取最新的数据。

Zookeeper实现配置管理的几个步骤:

配置存储

选取一个数据节点用于配置的存储,例如 /app1/database_config(如下简称“配置节点”),以下图:

咱们须要将配置信息写入该数据节点中,例如:

master0.jdbc.driverclass=com.mysql.jdbc.Driver
master0.jdbc.url=jdbc:mysql://xx.xx.xx.xx:3306/xx
master0.jdbc.username=xxxxxx
master0.jdbc.password=xxxxxx

slave0.jdbc.driverclass=com.mysql.jdbc.Driver
slave0.jdbc.url=jdbc:mysql://xx.xx.xx.xx:3306/xx
slave0.jdbc.username=xxxxxx
slave0.jdbc.password=xxxxxx
复制代码

配置获取 集群中每台机器在启动初始化阶段,首先会从这个数据节点中读取数据库的信息,同时,客户端还须要在该配置节点上注册一个数据变动的Watcher监听,一旦发生节点数据变动,全部订阅的客户端都可以获取到数据变动通知。

配置变动 系统运行过程当中,可能出现须要进行数据库切换的状况,这个时候就须要进行配置变动。咱们只须要对节点上的内容进行更新,Zookeeper就可以帮咱们将数据变动通知发送到各个客户端。

命名服务

分布式服务的名字相似于数据库中的惟一主键。利用Zookeeper节点建立的API能够建立一个顺序节点,而且在API返回值中会返回这个节点的完整名字。利用这个特性,能够借助Zookeeper来生成全局惟一ID。

如图所示,会有如下步骤:

1.全部客户端都会根据本身的任务类型,在指定类型的任务下面经过调用create()接口建立一个顺序节点,例如建立“job-”节点。

2.节点建立完毕后,create()接口返回完整节点名,例如“job-00000001”。

3.客户端拿到这个返回值后,拼接上type,例如“type2-job-0000001”,这就能够做为全局惟一ID。

分布式锁

使用Zookeeper来实现分布式锁,主要实现排它锁和共享锁两种。

排它锁

又称写锁或独占锁,核心是保证当前有且只有一个事务得到锁。

定义

经过在Zookeeper上创建一个数据节点,例如/exciusive_lock/lock节点就被定义为一个锁,如图:

获取

全部客户端试图建立临时节点,建立成功的任务该客户端获取了锁。同时,没有获取到锁的客户端在该节点上注册一个子节点变动的Watcher监听。

释放

由于是个临时节点,所以在两种状况下会删除此节点:

1.获取锁的客户端宕机

2.获取锁的客户端完成逻辑,主动删除

不管这两种哪一种方式释放锁,都会通知其余注册了的监听的客户端去从新获取锁。

所以,整个流程以下图所示:

共享锁

又称读锁,若是事务1对对象A加锁,那么其余的事务只能对A读,不能写。

定义

利用Zookeeper建立临时节点,名称相似为“/shared_lock/[Hostname]-请求类型-序号”。

以下图:

获取

在须要获取锁时,全部请求都在该节点下建立临时有序节点,若是是读的话就建立请求类型为R的,若是是写的就建立请求类型为W的。

锁的获取顺序

因为共享锁的定义,能够同时读,可是写时不能读也不能写的。锁的获取顺序分为以下四步:

1.建立节点后,获取/shared_lock节点下的全部子节点,并注册监听。

2.肯定本身的序号在全部节点中的顺序。

3.对于读请求:

    若是没有比本身小的节点,或是比本身小的节点都是读节点的话,就得到锁。

    若是比本身小的节点中有写节点,则继续等待。

 对于写请求:
    
    若是本身不是最小的节点,则继续等。

4.接收到监听的通知,继续1步骤。

复制代码

释放

释放逻辑和排它锁一致,很少赘述。

总体流程如图:

问题:羊群效应

上述共享锁的实现能知足10台机器之内的集群模型,若是规模扩大以后,会产生什么问题呢?

上述共享锁在竞争过程当中,存在大量的“watcher通知”和“子节点列表获取”两个重复操做,而且绝大数的运行结果都是判断本身不是最小的节点,从而继续等待。若是在规模较大的集群中,若是同一时间有多个节点对应的客户端完成事务或是事务中断引发节点消失,Zookeeper服务器就会在短期向客户端发送大量的通知---这就是羊群效应。

升级版共享锁

这里只须要改动:每一个锁竞争者,只需关注/shared_lock节点下序号比本身小的节点是否存在便可,具体实现以下:

1.客户端调用create()方法建立一个相似于“/shared_lock/[hostname]-请求类型-序号”的**临时有序节点**。
2.客户端调用getChildren()接口来获取全部已经建立的子节点列表,注意,这里不注册任何Watcher。
3.若是没法获取共享锁,那么就调用exist()来对比本身小的那个节点注册Watcher。
    注意,这里比本身小是个笼统的说法,具体对于读请求和写请求不同。
    读请求:向比本身序号小的最后一个写请求节点注册。
    写请求:向比本身小的最后一个节点注册。
4.等待Watcher通知,继续进入步骤2。
复制代码

流程如图所示:

第六章

····················持续更新中!!!!!!!!!!!!!·····················

相关文章
相关标签/搜索