【转】Paxos算法3-实现探讨

——转自:{老码农的专栏

  前两篇Paxos算法的讨论,让我们对paxos算法的理论形成过程有了大概的了解,但距离其成为一个可执行的算法程序还有很长的路要走,原因是很多的细节和错误未被考虑。Google Chubby的作者说,paxos算法实现起来远没有看起来简单,原因是paxos的容错仅限于server crash这一种情况,但在实际工程实现时要考虑磁盘损坏、文件损坏、Leader身份丢失等诸多的错误。

1. Paxos各角色的职能

  在paxos算法中存在Client、Proposer、Proposer Leaer、Acceptor、Learn五种角色,可精简为三种主要角色:proposer、acceptor、learn。角色只是逻辑上存在的,在实际实现中,节点可以身兼多职。

  在我们的讨论中,我们先假定没有Proposer Leader这一角色,在不考虑活锁的情况下,如果算法过程正确,那有Leader角色的算法过程肯定也正确。

  除了五种角色,还有三个重要的概念:instance、proposal、value,分别代表:每次paxos选举过程、提案、提案的value

当然,还有4个关键过程:

  • (Phase1):prepare
  • (Phase1):prepare ack
  • (Phase2):accept
  • (Phase2):accept ack

  对acceptor来说,还蕴含是着promise、accept、reject三个动作。

  先上一幅图,更直观地对几种角色的职能加以了解(各角色的具体职能参考Lamport的论文就足够了):

上图不是非常严格,仅为表现各角色之间的关系。

2. Proposer

  在Proposer、Acceptor、Learn中均涉及到proposal的编号,该编号应该有proposer作出改变,对其他的角色是只读的,这就保证了只有一个数据源。当多个proposer同时提交proposal时,必须保证各proposer的编号唯一、且可比较,具体做法之前已经提到。这里还要强调一点的是,仅每个proposer按自己的规则提高编号是不够的,还必须了解“外面”的最大编号是多少,例如,P1、P2、P3(请参考:Paxos算法2#再论编号问题:编号唯一性

  • P3的当前编号为初始编号2
  • 当P3提交proposal时发现已经有更大的编号16(16是P2提出的,按规则:5*3+1)
  • P3发起新编号时必须保证new no >16,按照前面的规则必须选择:5*3+2 = 17,而不能仅按自己的规则选择:1*3+2=5

  这要求acceptor要在reject消息中给出当前的最大编号,proposer可能出现宕机,重启后继续服务,reject消息会帮助它迅速找到下一个正确编号。但是当多个acceptor回复各自不一的reject消息时,事情就变得复杂起来。

当proposer发送proposal给一个acceptor时,会有三种结果:

  • timeout:超时,未接收到aceptor的response
  • reject:编号不够大,拒绝。并附有当前最大编号
  • promise:接受,并确保不会批准小于此编号的proposal。并附有当前最大编号及对应的value

  在判断是否可以进行Phase2时的一个充分条例就是:必须有acceptor的多数派promise了当前的proposal

  下面分别从Phase1和Phase2讨论proposer的行为:

Phase1-prepare:发送prepare到acceptor



Proposer在本地选择proposal编号,发送给acceptor,会收到几种情况的response:

(a). 没有收到多数派的回应

  消息丢失、Server宕机导致没有多数派响应,在可靠消息传输(TCP)下,应该报告宕机导致剩余的Server无法继续提供服务,在实际中一个多数派同时宕机的可能性非常小。

(b). 收到多数派的reject

  Acceptor可能会发生任意的错误,比如消息丢失、宕机重启等,会导致每个acceptor看到的最大编号不一致,因而在reject消息中response给proposer的最大编号也不一致,这种情况proposer应该取其最大作为比较对象,重新计算编号后继续Phase1的prepare阶段。

(c). 收到多数派的promise

根据包含的value不同,这些promise又分三种情况:

  • 多数派的value是相同的,说明之前已经达成了最终决议
  • value互不相同,之前并没有达成最终决议
  • 返回的value全部为null

  全部为null的情况比较好处理,只要proposer自由决定value即可;多数派达成一致的情况也好处理,选择已经达成决议的value提交即可,value互不相同的情况有两种处理方式:

  • 方案1:自由确定一个value。原因:反正之前没有达成决议,本次提交哪个value应该是没有限制的。
  • 方案2:选择promise编号最大的value。原因:本次选举(instance)已经有提案了,虽未获通过,但以后的提案应该继续之前的,Lamport在paxos simple的论文中选择这种方式。

  其实问题的本质是:在一个instance内,一个acceptor是否能accept多个value?约束P2只是要求,如果某个value v已被选出,那之后选出的还应该是v;反过来说,如果value v还没有被多数派accept,也没有限制acceptor只accept一个value。

  感觉两种处理方式都可以,只要选择一个value,能在paxos之后的过程中达成一致即可。其实不然,有可能value v已经成为了最终决议,但acceptor不知道,如果这时不选择value v而选其他value,会导致在一次instance内达成两个决议。

  会不会存在这样一种情况:A、B、C、D为多数派的promise,A、B、C的proposal编号,value为(1,1),D的为(2,2)?就是说,编号互不一致,但小编号的已经达成了最终决议,而大编号的没有?

设:小编号的proposal为P1,value为v1;大编号的proposal为P2,value为v2

  • 如果P1选出最终决议,那么肯定是完成了phase1、phase2。存在一个acceptor的多数派C1,P1为其最大编号,并且每个acceptor都accept了v1;
  • 当P2执行phase1时,存在多数派C2回应了promise,则C1与C2存在一个公共成员,其最大编号为P1,并且accept了v1
  • 根据规则,P2只能选择v1继续phase2,也就是说v1=v2,无论phase2是否能成功,绝不会在acceptor中遗留下类似(2,2)这样的value

  也就是说,只要按照【方案2】选择value就能保证结果的正确性。之所以能有这样的结果,关键还是那个神秘的多数派,这个多数派起了两个至关重要的作用:

  • 在phase1拒绝小编号的proposal
  • 在phase2强迫proposal选择指定的value

  而多数派能起作用的原因就是,任何两个多数派至少有一个公共成员,而这个公共成员对后续proposal的行为起着决定性的影响,如果这个多数派拒绝了后续的proposal,这些proposal就会因为无法形成新的多数派而进行不下去。这也是paxos算法的精髓所在吧。

Phase2-accept:发送accept给acceptor



  如果一切正常,proposer会选择一个value发送给acceptor,这个过程比较简单

accept也会收到2种回应:



(a). acceptor多数派accept了value

  一旦多数派accept了value,那最终决议就已达成,剩下的工作就是交由learn学习并关闭本次选举(instance)。

(b). acceptor多数派reject了value或超时

  说明acceptor不可用或提交的编号不够大,继续Phase1的处理。

proposer的处理大概如此,但实际编程时还有几个问题要考虑:

  • 来自acceptor的重复消息
  • 本来超时的消息又突然到了
  • 消息持久化

  其他2个问题比较简单,持久化的问题有必要讨论下。

  持久化的目的是在proposer server宕机“苏醒”时,可以继续参与paxos过程。

  从前面分析可看出,proposer工作的正确性是靠编号的正确性来保证的,编号的正确性是由proposer对编号的初始化写及acceptor的reject一起保证的,所以只要acceptor能正常工作,proposer就无须持久化当前编号。

3. acceptor

  acceptor的行为相对简单,就是根据提案的编号决定是否接受proposal,判断编号依赖promise和accept两种消息,因此acceptor必须对接收到的消息做持久化处理。根据之前的讨论也知道,acceptor的持久化也会影响着proposer的正确性。

  在acceptor对proposal进行决策的时候,还有个重要的概念没有被详细讨论,即instance。任何对proposal的判断都是基于某个instance,即某次paxos过程,当本次instance宣布结束(选出了最终决议)时,paxos过程就转移到下一个instance。这样会衍生出几个问题:

  1. instance何时被关闭?被谁关闭?
  2. acceptor的行为是否依赖instance的关闭与否?
  3. acceptor的多数派会不会在同一个instance内对两个不同的value同时达成一致?

  根据1中对各角色职能的讨论,决议是否被选出是由learn来决定的,当learn得知某个value v已经被多数派accept时,就认为决议被选出,并宣布关闭当前的instance。与2中提到的一样,因为网络原因acceptor可能不会得知instance已被关闭,而会继续对proposer回答关于该instance的问题。也就是说,无论如何acceptor都无法准确得知instance是否关闭,acceptor程序的正确性也就不能依赖instance是否关闭。但acceptor在已经知道instance已被关闭的情况下,在拒绝proposer时能提供更多的信息,比如,可以使proposer选择一个更高的instance重新提交请求。

  当然,只要proposer根据2中提到的方式进行提案,就不会发生同一instance中产生两个决议的情况。

4. learn

  learn的主要职责是学习决议,但决议必须一个一个按顺序学,不能跳号,比如learn已经知道了100,102号决议,必须同时知道101时才能一起学习。只是简单的等待101号决议的到来显然不是一个好办法,learn还要去主动向acceptor询问101号决议的情况,acceptor会对消息做持久化,做到这一点显然不难。

  learn还要周期性地check所接收到的value,一旦意识到决议已经达成,必须关闭对应的instance,并通知acceptor、proposer等(根据需要可以通知任意多的对象)。

  learn还存在一个问题是,是选择一个server做learn还是选多个,假如有N个acceptor,M个learn,则会产生N*M的通信量,如果M很大则通信量会非常大,如果M=1,通信量小但会形成单点。折中方案是选择规模相对较小的M,使这些learn通知其他learn。

paxos中的learn相对比较抽象,好理解但难以想象能做什么,原因在于对paxos的应用场景不清晰。一般说来有两种使用paxos的场景:

  • paxos作为单独的服务,比如google的chubby,hadoop的zookeeper
  • paxos作为应用的一部分,比如Keyspace、BerkeleyDB

  如果把paxos作为单独的服务,那learn的作用就是达成决议后通知客户端;如果是应用的一部分,learn则会直接执行业务逻辑,比如开始数据复制。

持久化:

  learn所依赖的所有信息就是value和instance,这些信息都已在acceptor中进行了持久化,所以learn不需要再对消息进行持久化,当learn新加入或重启时要做的就是能通过acceptor把这些信息取回来。

错误处理:

  learn可能会重启或新加入后会对“之前发生的事情”不清楚,解决办法是:使learn继续监听消息,直至某个instance对应的value达成一致时,learn再向acceptor请求之前所有的instance。

  至此,我们进一步讨论了paxos个角色的职责和可能的实现分析,离我们把paxos算法变为一个可执行程序的目标又进了一步,使我们对paxos的实现方式大致心里有底,但还有诸多的问题需要进一步讨论,比如错误处理。虽然文中也提到了一些错误及处理方式,但还没有系统地考虑到所有的错误。

接下来的讨论将重点围绕着分布式环境下的错误处理。

时间: 2024-12-28 09:32:42

【转】Paxos算法3-实现探讨的相关文章

Zookeeper学习之:paxos算法

paxos算法的重要性众所周知,它给如今的分布式一致性提供了迄今为止最好的解决方案.无论是Lamport自己的论文描述,还是网上的诸多资料,对paxos的描述都是及其简洁的,给人的感觉是paxos看似很简单,但是细深究起来又不是那么的具象,因为单纯的文字描述还是略显抽象,因此,我会先分别从文字概念描述和伪代码的方式分别阐述paxos算法的概念思想,对比着看,可以加深对paxos的理解,后期我会结合PhxPaxos代码来进一步探讨paxos工程化过程中一些实践问题. 首先来看paxos算法的文字版

Paxos算法再思考

1. 背景 在学习分布式相关知识时,很早之前就断断续续了解过Paxos算法,搜到的资料大抵如Paxos Made Simple中译版,互相转载.翻译.互相注释.在粗览版面后,发现了一些奇怪的东西,诸如“提案”.“选举”.“议员”.“决议”.心里不禁骂娘,这都是些神马玩意啊,和分布式容错有五毛钱关系?作为一个长期生活在社会主义国家的单纯码农,瞬间被这些词汇带歪了.硬着头皮往后读,发现遇到疑惑的地方又冷不丁一个“显而易见”,真真有苦无处诉. 这还算好,如果你看到其他作者拿出三国时期的五虎上将来举例,

【转】Paxos算法细节详解

最近研究zookeeper因此就学习了一下paxos算法,一直没太看懂,今天找到了这篇文章描述的很清晰,转自:http://www.cnblogs.com/endsock/p/3480093.html Paxos分析 最近研究paxos算法,看了许多相关的文章,概念还是很模糊,觉得还是没有掌握paxos算法的精髓,所以花了3天时间分析了libpaxos3的所有代码,此代码可以从https://bitbucket.org/sciascid/libpaxos 下载.对paxos算法有初步了解之后,再

Paxos算法

转载请注明出处http://www.cnblogs.com/dvd0423/p/4185697.html 浮躁的人请绕道,这篇文章不适合你. 把Paxos作为分布式系统系列的第一篇,是因为我迫不及待的想告诉别人这个算法是多么牛逼.相比于Map/Reduce架构.一致性Hash.两阶段提交等等,我觉得Paxos是一个让人第一次看到会感觉废话连篇,再仔细品味会拍手称赞的算法.Paxos算法最初由lamport提出,他第一次提出时模拟的是一个叫Paxos的希腊城邦立法的问题.由于这个算法太抽象了,所以

一步一步理解Paxos算法

一步一步理解Paxos算法 背景 Paxos 算法是Lamport于1990年提出的一种基于消息传递的一致性算法.由于算法难以理解起初并没有引起人们的重视,使Lamport在八年后重新发表到 TOCS上.即便如此paxos算法还是没有得到重视,2001年Lamport用可读性比较强的叙述性语言给出算法描述.可见Lamport对 paxos算法情有独钟.近几年paxos算法的普遍使用也证明它在分布式一致性算法中的重要地位.06年google的三篇论文初现“云”的端倪,其中的chubby锁服务使用p

【转】Paxos算法深入分析

http://blog.csdn.net/anderscloud/article/details/7175209 在分布式系统设计领域,Paxos可谓是最重要一致性的算法.Google的大牛们称   All working protocols for asynchronous consensus we have so far encountered have Paxos at their core. 可见此算法的地位.网络上讨论此算法的文章多如牛毛,但大多数让人看了之后仍然是一头雾水,就连维基百

【转】Paxos算法1-算法形成理论

——转自:{老码农的专栏} Paxos算法的难理解与算法的知名度一样令人敬仰,从我个人的经历而言,难理解的原因并不是该算法高深到大家智商不够,而在于Lamport在表达该算法时过于晦涩且缺乏一个完整的应用场景.如果大师能换种思路表达该算法,大家可能会更容易接受: 首先提出算法适用的场景,给出一个多数读者能理解的案例 其次描述Paxos算法如何解决这个问题 再次给出算法的起源(就是那些希腊城邦的比喻和算法过程) Lamport首先提出算法的起源,在没有任何辅助场景下,已经让很多人陷于泥潭,在满脑子

分布式理论之一:Paxos算法的通俗理解

维基的简介:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的"La",此人现在在微软研究院)于1990年提出的一种基于消息传递且具有高度容错特性的一致性算法. Paxos算法目前在Google的Chubby.MegaStore.Spanner等系统中得到了应用,Hadoop中的ZooKeeper也使用了Paxos算法,在上面的各个系统中,使用的算法与Lamport提出的原始Paxos并不完全一样,这个以后再慢慢分析.本博文的目的是,如何让一个小白在半

底层算法系列:Paxos算法

关于算法,面太广.本系列只研究实际应用中遇到的核心算法.了解这些算法和应用,对java码农进阶是很有必要的. 对于Paxos学习论证过程中,证实一句话:有史以来学习paxos最好的地方wiki:Paxos (computer science) 目录 1.背景 2.Paxos算法 3.Muti-Paxos算法 4.Muti-Paxos在google chubby中的应用 ===============正文分割线============================ 一.背景 Paxos 协议是一