2019独角兽企业重金招聘Python工程师标准>>>

Paxos算法是大名鼎鼎的Zookeeper中采用的选举Leader的算法,事实上,在涉及到分布式系统的一致性的时候,就只有一种算法,那就是Paxos.

首先来看,Paxos是为了解决什么问题:

Paxos 算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。一个典型的场景是,在一个分布式数据库存储中,如果各节点的初始状态一致,每个节点执行相同的操作序列,那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个“一致性算法”以保证每个节点看到的指令一致。一个通用的一致性算法可以应用在许多场景中,是分布式计算中的重要问题。因此从20世纪80年代起对于一致性算法的研究就没有停止过。节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。Paxos 算法就是一种基于消息传递模型的一致性算法。

本文的实现参考了LynnCui在知乎上的文章:https://zhuanlan.zhihu.com/p/21438357

有关Paxos的详细介绍及推理过程均可参考上述文章,如果还有不明白,可以查阅相关文章,这一类的文章网上还是比较多的。

关于我的实现,我这里有几点需要说明:

1.关于maxVote(即比本次表决编号小的最大表决编号),如果该编号存在,则应该用改编号对应表决的提案作为下一次的提案,否则,则可以随机指定提案,具体的提案的指定方式依赖于需求逻辑,本程序中采用随机提案;

2.Paxos假设消息的传递过程是不可靠的,这主要是因为实际环境中,网络通信是没有办法保障的。网络延迟以及服务器本身也有可能宕机,这些条件其实都在Paxos的前提假设之中,本程序当中做了一个假设,假设网络通信有50%的概率失败,实际上是我随便指定的;

3.Java本身是一门繁琐的语言,所以难免会有很大冗余代码,这主要是为了保证程序的稳定性,还有一部分是出于习惯。Paxos其实并不是一个复杂的算法,至少基本的不是。程序中所采用的的面向对象的设计也不一定是合理的;

4.本程序依赖了Google的guava包以及common-lang3的包,实际中完全可以去掉。guava主要是在重载hashCode()方法的时候用到,但程序中并没有使用hashCode的地方,实际只是因为在重载equals()方法的时候总是应该重载hashCode()方法。lang3主要做了字符串比较,应对字符串为空的情况。除此之外没有任何依赖。

Java实现参考:

public final class PaxosDemo {private static final HashFunction HASH_FUNCTION = Hashing.murmur3_32();private static final Random RANDOM = new Random();private static final String[] PROPOSALS = {"ProjectA", "ProjectB", "ProjectC"};public static void main(String[] args) {List<Acceptor> acceptors = new ArrayList<Acceptor>();Arrays.asList("A", "B", "C", "D", "E").forEach(name -> acceptors.add(new Acceptor(name)));Proposer.vote(new Proposal(1L, null), acceptors);}private static void printInfo(String subject, String operation, String result) {System.out.println(subject + ":" + operation + "<" + result + ">");}/*** 对于提案的约束,第三条约束要求:* 如果maxVote不存在,那么没有限制,下一次表决可以使用任意提案;* 否则,下一次表决要沿用maxVote的提案** @param currentVoteNumber* @param proposals* @return*/private static Proposal nextProposal(long currentVoteNumber, List<Proposal> proposals) {long voteNumber = currentVoteNumber + 1;if (proposals.isEmpty())return new Proposal(voteNumber, PROPOSALS[RANDOM.nextInt(PROPOSALS.length)]);Collections.sort(proposals);Proposal maxVote = proposals.get(proposals.size() - 1);long maxVoteNumber = maxVote.getVoteNumber();String content = maxVote.getContent();if (maxVoteNumber >= currentVoteNumber)throw new IllegalStateException("illegal state maxVoteNumber");if (content != null)return new Proposal(voteNumber, content);else return new Proposal(voteNumber, PROPOSALS[RANDOM.nextInt(PROPOSALS.length)]);}private static class Proposer {/*** @param proposal* @param acceptors*/public static void vote(Proposal proposal, Collection<Acceptor> acceptors) {int quorum = Math.floorDiv(acceptors.size(), 2) + 1;int count = 0;while (true) {printInfo("VOTE_ROUND", "START", ++count + "");List<Proposal> proposals = new ArrayList<Proposal>();for (Acceptor acceptor : acceptors) {Promise promise = acceptor.onPrepare(proposal);if (promise != null && promise.isAck())proposals.add(promise.getProposal());}if (proposals.size() < quorum) {printInfo("PROPOSER[" + proposal + "]", "VOTE", "NOT PREPARED");proposal = nextProposal(proposal.getVoteNumber(), proposals);continue;}int acceptCount = 0;for (Acceptor acceptor : acceptors) {if (acceptor.onAccept(proposal))acceptCount++;}if (acceptCount < quorum) {printInfo("PROPOSER[" + proposal + "]", "VOTE", "NOT ACCEPTED");proposal = nextProposal(proposal.getVoteNumber(), proposals);continue;}break;}printInfo("PROPOSER[" + proposal + "]", "VOTE", "SUCCESS");}}private static class Acceptor {//上次表决结果private Proposal last = new Proposal();private String name;public Acceptor(String name) {this.name = name;}public Promise onPrepare(Proposal proposal) {//假设这个过程有50%的几率失败if (Math.random() - 0.5 > 0) {printInfo("ACCEPTER_" + name, "PREPARE", "NO RESPONSE");return null;}if (proposal == null)throw new IllegalArgumentException("null proposal");if (proposal.getVoteNumber() > last.getVoteNumber()) {Promise response = new Promise(true, last);last = proposal;printInfo("ACCEPTER_" + name, "PREPARE", "OK");return response;} else {printInfo("ACCEPTER_" + name, "PREPARE", "REJECTED");return new Promise(false, null);}}public boolean onAccept(Proposal proposal) {//假设这个过程有50%的几率失败if (Math.random() - 0.5 > 0) {printInfo("ACCEPTER_" + name, "ACCEPT", "NO RESPONSE");return false;}printInfo("ACCEPTER_" + name, "ACCEPT", "OK");return last.equals(proposal);}}private static class Promise {private final boolean ack;private final Proposal proposal;public Promise(boolean ack, Proposal proposal) {this.ack = ack;this.proposal = proposal;}public boolean isAck() {return ack;}public Proposal getProposal() {return proposal;}}private static class Proposal implements Comparable<Proposal> {private final long voteNumber;private final String content;public Proposal(long voteNumber, String content) {this.voteNumber = voteNumber;this.content = content;}public Proposal() {this(0, null);}public long getVoteNumber() {return voteNumber;}public String getContent() {return content;}@Overridepublic int compareTo(Proposal o) {return Long.compare(voteNumber, o.voteNumber);}@Overridepublic boolean equals(Object obj) {if (obj == null)return false;if (!(obj instanceof Proposal))return false;Proposal proposal = (Proposal) obj;return voteNumber == proposal.voteNumber && StringUtils.equals(content, proposal.content);}@Overridepublic int hashCode() {return HASH_FUNCTION.newHasher().putLong(voteNumber).putString(content, Charsets.UTF_8).hash().asInt();}@Overridepublic String toString() {return new StringBuilder().append(voteNumber).append(':').append(content).toString();}}}

以下是我试着运行了一下程序的结果:

VOTE_ROUND:START<1>
ACCEPTER_A:PREPARE<OK>
ACCEPTER_B:PREPARE<NO RESPONSE>
ACCEPTER_C:PREPARE<OK>
ACCEPTER_D:PREPARE<NO RESPONSE>
ACCEPTER_E:PREPARE<NO RESPONSE>
PROPOSER[1:null]:VOTE<NOT PREPARED>
VOTE_ROUND:START<2>
ACCEPTER_A:PREPARE<OK>
ACCEPTER_B:PREPARE<NO RESPONSE>
ACCEPTER_C:PREPARE<OK>
ACCEPTER_D:PREPARE<NO RESPONSE>
ACCEPTER_E:PREPARE<OK>
ACCEPTER_A:ACCEPT<OK>
ACCEPTER_B:ACCEPT<NO RESPONSE>
ACCEPTER_C:ACCEPT<NO RESPONSE>
ACCEPTER_D:ACCEPT<OK>
ACCEPTER_E:ACCEPT<OK>
PROPOSER[2:ProjectC]:VOTE<NOT ACCEPTED>
VOTE_ROUND:START<3>
ACCEPTER_A:PREPARE<OK>
ACCEPTER_B:PREPARE<OK>
ACCEPTER_C:PREPARE<OK>
ACCEPTER_D:PREPARE<OK>
ACCEPTER_E:PREPARE<OK>
ACCEPTER_A:ACCEPT<OK>
ACCEPTER_B:ACCEPT<NO RESPONSE>
ACCEPTER_C:ACCEPT<NO RESPONSE>
ACCEPTER_D:ACCEPT<NO RESPONSE>
ACCEPTER_E:ACCEPT<NO RESPONSE>
PROPOSER[3:ProjectB]:VOTE<NOT ACCEPTED>
VOTE_ROUND:START<4>
ACCEPTER_A:PREPARE<NO RESPONSE>
ACCEPTER_B:PREPARE<NO RESPONSE>
ACCEPTER_C:PREPARE<OK>
ACCEPTER_D:PREPARE<OK>
ACCEPTER_E:PREPARE<OK>
ACCEPTER_A:ACCEPT<OK>
ACCEPTER_B:ACCEPT<OK>
ACCEPTER_C:ACCEPT<NO RESPONSE>
ACCEPTER_D:ACCEPT<OK>
ACCEPTER_E:ACCEPT<OK>
PROPOSER[4:ProjectC]:VOTE<NOT ACCEPTED>
VOTE_ROUND:START<5>
ACCEPTER_A:PREPARE<OK>
ACCEPTER_B:PREPARE<OK>
ACCEPTER_C:PREPARE<NO RESPONSE>
ACCEPTER_D:PREPARE<NO RESPONSE>
ACCEPTER_E:PREPARE<OK>
ACCEPTER_A:ACCEPT<NO RESPONSE>
ACCEPTER_B:ACCEPT<OK>
ACCEPTER_C:ACCEPT<NO RESPONSE>
ACCEPTER_D:ACCEPT<NO RESPONSE>
ACCEPTER_E:ACCEPT<NO RESPONSE>
PROPOSER[5:ProjectB]:VOTE<NOT ACCEPTED>
VOTE_ROUND:START<6>
ACCEPTER_A:PREPARE<NO RESPONSE>
ACCEPTER_B:PREPARE<OK>
ACCEPTER_C:PREPARE<OK>
ACCEPTER_D:PREPARE<OK>
ACCEPTER_E:PREPARE<NO RESPONSE>
ACCEPTER_A:ACCEPT<NO RESPONSE>
ACCEPTER_B:ACCEPT<NO RESPONSE>
ACCEPTER_C:ACCEPT<NO RESPONSE>
ACCEPTER_D:ACCEPT<NO RESPONSE>
ACCEPTER_E:ACCEPT<NO RESPONSE>
PROPOSER[6:ProjectC]:VOTE<NOT ACCEPTED>
VOTE_ROUND:START<7>
ACCEPTER_A:PREPARE<OK>
ACCEPTER_B:PREPARE<OK>
ACCEPTER_C:PREPARE<OK>
ACCEPTER_D:PREPARE<OK>
ACCEPTER_E:PREPARE<NO RESPONSE>
ACCEPTER_A:ACCEPT<OK>
ACCEPTER_B:ACCEPT<OK>
ACCEPTER_C:ACCEPT<OK>
ACCEPTER_D:ACCEPT<NO RESPONSE>
ACCEPTER_E:ACCEPT<NO RESPONSE>
PROPOSER[7:ProjectB]:VOTE<SUCCESS>

经过7轮表决终于达成一致.

转载于:https://my.oschina.net/u/2541538/blog/807185

朴素Paxos(Basic Paxos)算法java简易实现相关推荐

  1. 一致性协议Paxos详解(一):Basic Paxos协议详解

    一致性协议Paxos详解(一):Basic Paxos协议详解 前言 Paxos是什么 Paxos算法原理与推导 Basic Paxos Proposal Numbers prepare阶段 prep ...

  2. 理解分布式一致性:Paxos协议之Basic Paxos

    理解分布式一致性:Paxos协议之Basic Paxos 角色 Proposal Number & Agreed Value Basic Paxos Basic Paxos without f ...

  3. Paxos算法(Basic Paxos 与 Multi-Paxos思想)

    目录 Basic Paxos 三个角色 达成共识的方法 对于Basic Paxos的总结 Multi-Paxos 领导者 优化 Basic Paxos 执行 reference Paxos 算法包含 ...

  4. Basic Paxos算法

    Basic Paxos算法 背景 Paxos算法是Lamport于1990年提出的一种基于消息传递的一致性算法.由于算法难以理解起初并没有引起人们的重视,使Lamport在八年后重新发表到TOCS上. ...

  5. Paxos - 分布式表决算法

    原文链接:http://en.wikipedia.org/wiki/Paxos_(computer_science) Paxos (computer science) From Wikipedia, ...

  6. Paxos协议之Basic Paxos/Multi Paxos

    论文Paxos made simple从简单易懂的角度阐述了Paxos算法.之后,2013年斯坦福大学的Diego Ongaro做了一个讲座:Implementing Replicated Logs ...

  7. 深入浅出Paxos分布式一致性算法

    深入浅出Paxos分布式一致性算法

  8. 分布式一致性算法——Paxos 和 Raft 算法

    写在前面 本文隶属于专栏<100个问题搞定大数据理论体系>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见100个问题搞定大数据理 ...

  9. basic paxos,multi paxos

    1.basic paxos: 集群内所有节点如何就一个值达成共识 角色: 1.提议者:提出提议(proposal). 2.接受者:对于提议要给出自己的回答. 3.学习者:接受达成共识的值,不参与投票和 ...

最新文章

  1. sql镶嵌查询_sql数据库的嵌套查询
  2. springboot webjar使用
  3. 修改oracle数据库默认时间格式
  4. 短视频进入下半场,价值创造成赛点
  5. github(入门),不入门找卢姥爷
  6. c语言 方法重载 冲定义,C++ 重载(overload)、重写(overrride)、重定义(redefine)
  7. spring拦截器-过滤器的区别
  8. mp4 拍摄时间如何看_时间不多了,如何备考期末最有效?这些复习技巧,看了你就会了...
  9. 解决“计划任务不存在的问题”方法
  10. 《Python 快速入门》一千个程序员有一千套编码规范
  11. 山东工业大学计算机及应用,彭玉旭副教授
  12. python数值类型的操作_Python学习笔记,数值类型及操作
  13. 《BUG创造队》作业9:【Beta】冲刺 Scrum meeting 1
  14. R语言编程艺术(4)R对数据、文件、字符串以及图形的处理
  15. 4.2 文本特征抽取的两种方式CountVectorizer与TfidfVectorizer
  16. sql server添加列
  17. iMeta教你绘图 | 世界海拔地图
  18. 局域网ip扫描工具_IP Scanner局域网IP扫描工具
  19. 深度学习之openvino预训练模型测试(车牌识别)
  20. 平稳/非平稳信号举例

热门文章

  1. 华为计算机如何计算sina,一道华为的“简单四则运算”题
  2. java添加文字水印插件_java之io添加文字水印
  3. Sun地系统架构师考试(SCEA)
  4. ECMAScript 对象类型
  5. racle的STRUCT和VARRAY的读写方法
  6. oracle的rank,over partition 使用 和lead
  7. 游戏201712-2
  8. STL - bitset
  9. RabbitMQ 上手记录-part 1-基础概念
  10. QT5的exe的发布