区块链的底层原理研究(1)分布式系统 Paxos协议解析

分布式系统研究简介:

随着计算及技术的发展,如今几乎所有实际应用的计算机系统都是分布式的。分布式系统的理论研究其实是从Leslie Lamport在1989年提出关于Paxos算法的论文开始的。这篇论文企图用一个寓言的方式来描述Paxos算法;但由于其理论过于超前,阐述方式非常奇特,导致其很长一段时间不被学术界接受和认可。从1996年开始,学术界才开始大量的分布式计算的理论研究,2001年Lampson发表的论文“The ABCD's of Paxos"对当时的Paxos理论进行了分类和集中地描述,奠定了分布式系统的理论基础。紧接着产业界,主要是互联网公司对大规模分布式计算机系统的需求和应用,业界对这些基础理论的应用和实践进一步推动了其发展,理论和实践的结合,催生出了大量的成果。Google公司由于自身的需要,在这个过程中扮演了尤其重要的角色。现在,存储,计算相关的分布式系统很大程度上都是Google应用和研究成果的使用和衍生,比如BigTable,MapReduce,Chubby/Zookeeper(Yahoo)等等。

区块链的两个理论基石是分布式系统和加密和密码学,在这篇文章里,会重点讨论分布式系统相关的内容。分布式系统能够比较容易得横向扩大存储容量,计算能力以及连接不同的地理区域。但遇到的最大问题就是系统协同性问题(Coordination)。在不同的系统中有不同的体现。区块链中主要是一致性,共识,账单,协议... 在其他的系统中容错性,可恢复性和可用性也是常见的问题。

Paxos算法:
1. 分布式系统的构成:

系统中的一个工作单元通常被称作一个节点(Node),节点通过通讯网络连接起来,节点之间通过消息机制传递信息和命令。传统的网络服务分为服务器和客户端两种节点,P2P网络中每个计算机都是一个平等的节点(同时有服务器和客户端的功能)。区块链网络通常都是由P2P节点组成的,这其实和其去中心化的需求相关,区块链网络中不能够也不应该存在一个或几个集中计算和存储资源的中心服务器/集群(DPOS的区块链系统【如EOS】其实并不符合区块链技术去中心化的基本原则,我觉得应该找到一个更合理的解决方案,这个会在后面的文章讨论)。

2. 消息传递:

分布式系统中的节点之间必须能够互相通讯,通讯是通过消息传递的形式完成的。作为客户端的节点向服务端节点发送请求消息;作为服务端的节点接受到请求消息,并处理消息。消息传递机制貌似比较简单,但实际上衍生出很多需要解决的问题:

  • 消息损坏和丢失:对于分布式系统,一个重要假设就是消息传输的通道不是100%可靠的。那么在消息传递的过程中就会出现两种情况,第一种是消息损坏,也就是送达服务器节点的消息和原始消息不同,这种情况比较容易处理,只要在消息包中加入校验码就可以确定消息是否损坏。第二种情况是消息丢失,没有送达服务器节点,这种情况比较难以处理,通常的做法是采用带确认的消息机制,服务端收到消息后会发给客户节点一个确认消息,如果客户节点没有在一个合理时间收到确认消息,则会从新发送这条消息。带确认的消息机制是很多底层网络协议的基础,比如TCP协议。
  • 消息延迟:在实际的系统中,每条消息传递所需的时间是不同的,即使是相同节点之间的消息传递也会有不同的延迟时间。如果存在多个客户端同时发送命令,那到达服务器的顺序是不可知的。如果这些消息(命令)的执行是有顺序的,那就会导致不一致情况的出现,比如c1发送x=x+5命令,c2发出x=x*2的命令,这两条命令执行顺序不同,结果会完全不同。如果按到达先后的顺序执行命令,则显然是不可靠的。对于这种情况的处理,比较简单得方式是通过一个串行化器(Serializer)接受命令,所有客户端的命令发送给串行化器,串行化器将命令排序后提交服务器执行,完成后将执行结果返回所有客户端。这种串行化器方式也被称为主从复制模式(Master-Slave Replication)。这种方式解决了不一致问题,但是却引发了串行化器的单点故障问题。
  • 节点故障:上面已经介绍了一种节点故障,叫做单点故障。除了单点故障以外,其实每一个节点都是可能出现故障,无法正常运行的,对于大量节点的互联网应用,哪怕每个节点只有极小概率出现故障的情况下,有大量节点组成的整个系统仍然有非常大的概率出现故障节点。分布式系统必须能够容忍一定比例的节点故障。
3. 两阶段协议和Paxos:

通过上面的描述,我们其实看到对于分布式系统来讲,主要需要解决两个问题。一,消息传输通道的不可靠性;二,节点或单点故障造成的问题。提出问题,下一步就是对问题的解决:

1. 采用两阶段协议的分布式系统:

前面讲到对于命令执行的不一致性,我们引入了一种串行化器的解决方案(主从模式),对于一个小规模的分布式系统,通过采用高可靠性的设备和高性能灾备系统,这个方案应该已经足够胜任了。但是对于一个大规模的分布式系统,这个单点的串行化器无论从可靠性,性能还是安全性的角度来看都是一个极大的风险。两阶段协议将这个问题换了一个解决思路,把发送命令的权利下放给客户端节点,但是客户端节点必须获得所有服务器的锁的情况下才能够发命令,一旦服务器执行命令完毕,客户端收到确认通知则立即释放锁,其他的客户端可以获取锁并发送命令。这样保证在同一时间只有一个客户端节点能够给服务器发送命令:阶段一:客户端获取服务端的锁,第二阶段:客户端向服务端发送命令并执行。

两阶段协议避免使用串行化器解决了不一致问题。数据库事务事实上也是一个两阶段协议的实现,第一个阶段是准备(preparation)第二个阶段是提交(commit),但是数据库提交阶段通常还是在数据库服务器上进行的,这是两阶段协议的一种变化(PS:任何理论在实际的应用中都是会根据具体问题和情况变化的,理论的意义在于提供思路和方向,而不是照搬照抄,在工作中遇到两种错误的态度1. 只是注意实际的系统,实际的问题,从不或者很少关注和提炼理论上的东西,这叫只修招式,不练内力,只能是花拳绣脚,知其然,不知其所以然。2. 重视标准和理论,任何系统的实现都一定要遵从现有论文上的理论模式或者标准中的实现,这叫不知变通,认死理做死工,更不可取)。

两阶段协议是否解决了单点故障的问题呢?我觉得没有完全解决,需要进一步的改进才可以。两阶段协议本身只是提供服务器锁的机制,但是并没有提供如何判断一个命令是否能够执行的标准。如果有多个服务器的情况下(在区块链网络中这个非常常见,一个节点如果产生新的区块,一定会通知其他所有连接的节点更新主链),是否需要所有服务器的锁才能够提交命令,还是只要获得半数以上的服务器锁就可以提交?如何处理和其他客户端发出锁请求的冲突?如果某些客户端在释放锁之前就宕机如何处理?可以看到两阶段协议提出了一个不错的思路,但是同时也引入了更多的问题。

2. Paxos

Paxos 引入了票(Ticket)的机制,实际上是对锁机制的一种弱化,服务器随时会可以生成一张新的票,客户端可以请求并得到最新的票,前面产生的票没有过期,仍然可以生成新的票。一旦接收到客户端给服务端发送带有票号的消息后,该票号过期。Paxos协议能够更好得应对分布式系统中的节点故障,最简单的Paxos协议分成三个阶段执行。

  • 第一阶段:客户端向所有服务端请求一张票。
  • 第二阶段:if 客户端收到过半数服务器发回的票,then 客户端将命令和票号同时发回服务器,服务器一旦收到票,检查是否有效,如果有效则存储命令并反馈success消息给客户端。
  • 第三阶段:if 客户端收到过半数服务器的success反馈,then 客户端提交给所有服务器执行命令的指令。指令完成,获得结果,返回第一阶段。

这个朴素(简单)的Paxos协议是通过服务端来产生票,客户端请求票。这个机制的一个问题是,在获得半数以上的客户端c1写入命令u1后,另外一个客户端c2可能获得更新的票号,并给某些服务器写入了新的命令u2. 于是执行阶段某些服务器会执行c2。这个问题可以通过服务器向c2发送票号的同时发送命令u1来解决。如果u1命令已经获得半数以上服务器的票,c2就会转而支持c1。但是在没有任何客户端获得半数票的情况下,所有的客户端都会竞争指令的执行权。问题是如果客户端c2从不同的服务器得到不止u1一个命令,客户端应该选择支持哪一个命令呢?同时还有个问题是服务器都是独立维护自己的票号,这样造成无法产生全局的唯一票号,造成的结果就是不知道哪个是最新产生的命令。通常对于分布是系统而言,支持最新的产生的命令是最安全的做法。于是引出了修改的Paxos协议。

修改版Paxos协议:

这个协议中由客户端生成当前票号,这样能够保证最新票号的唯一性。

  • 初始化:客户端存储命令c,生成当前票号t+1。服务端存储当前记录的最大票号T, 当前存储的命令C以及该命令的票号Tc
  • 第一阶段:客户端向所有服务端发送消息,获的当前t=t+1的票号。服务端收到请求,如果t>T, 则T=t,返回ok,同时返回当前存储的命令+票号Tc。
  • 第二阶段:收到半数服务器回复 ok。如果最大票号Tc-max != 0,选择支持Tc-max所对应的指令C; 如果Tc-max =0,说明当前系统中没有存储待执行的命令,C写入原来准备提交的命令c。回复所有返回ok的服务器,提议(Tc-max,C)。服务器端,如果t=T, 则更新Tc = T。返回客户端success。
  • 第三阶段:如果过半数服务器回复success,则向所有服务端发送执行指令。 

Paxos协议确保了所有节点总是支持票号最大的命令(提议),以达成一致性,同时也可以很好地应对节点故障。无论客户端还是服务端故障,都不会使系统失效。如果一个客户端在执行阶段前宕机,仍然可以保证最新的过半数命令得到执行,即使这个指令最早是宕机客户端最早提交的。同时只要不是半数以上服务端故障,整个分布式系统仍然能够正常运行。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rocky-Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值