使用去实现Paxos共识算法的方法

  

  最初的服务往往都是通过单体架构对外提供的,即单服务器——单数据库模式。随着业务的不断扩展,用户和请求数都在不断上升,如何应对大量的请求就成了每个服务都需要解决的问题,这也就是我们常说的高并发。为了解决单台服务器面对高并发的苍白无力,可以通过增加服务器数量来解决,即多服务器——单数据库(主从)模式,此时的压力就来到了数据库一方,数据库的IO效率决定了整个服务的效率,继续增加服务器数量将无法提升服务性能。这就衍生出了当前火热的微服务架构。当用户请求经由负载均衡分配到某一服务实例上后,如何保证该服务的其他实例最终能够得到相同的数据变化呢?这就要用到Paxos分布式共识协议,Paxos解决的就是共识问题,也就是一段时间后,无论得到哪一个服务实例,都能获取到相同的数据。目前国内外的分布式产品很多都使用了Paxos协议,可以说Paxos几乎就是共识协议的标准和代名词。

   Paxos有两种协议,我们常常提到的其实是基本Paxos,另一种叫多Paxos,如无特殊说明,本文中提到的Paxos协议均为基本Paxos。

   Paxos协议是由图灵奖获得者Leslie Lamport于1998年在其论文《兼职议会》中首次提出的,讲述了一个希腊小岛Paxos是如何通过决议的。但由于该论文晦涩艰深,当时的计算机界大牛们也没几个人能理解。于是Lamport2001年再次发表了《Paxos简单》,摘要部分是这么写的:

   Paxos算法,提出了在普通英语,很简单。

  翻译过来就是:不会吧,不会吧,这么简单的Paxos算法不会真的有人弄不懂吧?然而事实却是很多人对Paxos都望而却步,理解Paxos其实并不难,但是Paxos的难点在于工程化,如何利用Paxos协议写出一个能过够真正在生产环境中跑起来的服务才是Paxos最难的地方,关于Paxos的工程化可以参考微信后台团队撰写的《微信自研生产级Paxos类库PhxPaxos实现原理介绍》

  

   Paxos协议一共有两个阶段:准备和提出,两种角色:申请人和承兑人,每一个服务实例既是申请人,同时也是受体,申请人负责提议,受体决定是否接收来自申请人的提议,一旦提议被多数接受,那么我们就可以宣称对该提议包含的值达成了一致,而且不会再改变。

  阶段一:准备准备

  
      <李数据线=" 20 ">申请人生成全局唯一ProposalID(时间戳+ ServerID)   <李数据线=?1”>申请人向所有受体(包括申请人自己)发送准备(n=ProposalID)请求李   <李数据线=" 22 ">受体比较n和minProposal,如果n比;minProposal minProposal=n,受体返回已接受的提议(acceptedProposal acceptedValue)   <李数据线=" 23 ">承诺1:不再接受n & lt;=minProposal的准备请求李   <李数据线=" 24 ">承诺2:不再接受n & lt;李minProposal的提议请求   <李数据线=" 25 ">应答1:返回此前已接受的提议李   <李数据线=" 26 ">当申请人收到大于半数的返回后李   <李数据线=?7”>准备请求被拒绝,重新生成ProposalID并发送准备请求李   <李数据线=?8”>准备请求被接受且有已接受的提议,选择最大的ProposalID对应的值作为提议的值   <李数据线=" 29 ">准备请求被接受且没有已接受的提议,可选择任意提议值   阶段二:提出提议

      李   <李数据线=?3”>申请人向所有受体(包括申请人自己)发送接受(n=ProposalID价值=https://www.yisu.com/zixun/ProposalValue)请求李   <李数据线=?4”>受体比较n和minProposal,如果n祝辞=minProposal minProposal=n, acceptedValue=https://www.yisu.com/zixun/value,返回已接受的提议(minProposal acceptedValue)   <李数据线=" 35 ">当申请人收到大于半数的返回后李   <李数据线=?6”>提出请求被拒绝,重新生成ProposalID并发送准备请求李   <李数据线=" 37 ">提出请求被接受,则数据达成一致性李   
  

  

需要注意的是,在一个服务集群中以上两个阶段是很有可能同时发生的,例如:实例一个已完成准备阶段,并发送了提出请求。同时实例B开始了准备阶段,并生成了更大的ProposalID发送准备请求,可能导致实例一的建议请求被拒绝。每个服务实例也是同时在扮演申请人和受体角色,向其他服务发送请求的同时,可能也在处理别的服务发来的请求。

  

  

服务注册与发现

  

由于每个服务实例都是在执行相同的代码,那我们要如何知晓其他服务实例的入口呢(IP和端口号)?方法之一就是写死在代码中,或者提供一份配置文件。服务启动后可以读取该配置文件。但是这种方法不利于维护,一旦我们需要移除或添加服务则需要在每个机器上重新休息配置文件。

使用去实现Paxos共识算法的方法