Java中的分布式一致性与共识算法

embedded/2024/9/20 7:37:12/ 标签: java, 分布式, 共识算法

分布式系统中,节点之间必须就某些值达成一致。但由于网络的不可靠性、节点故障以及其他不可预测因素,实现一致性变得极为复杂。共识算法应运而生,旨在解决这一难题。本文将深入探讨两种主要的共识算法——Paxos和Raft,解释其原理,并提供Java代码示例。此外,我们还将对比它们的优缺点,以帮助你选择最适合的算法。

1. 分布式一致性概述

分布式一致性是指在多个节点之间达成一致的能力,即所有节点都能看到相同的数据状态。为了实现分布式一致性,共识算法成为关键。它们通过节点间的通信,确保在分布式系统中的所有节点达成一致。

2. Paxos算法
2.1 Paxos基本原理

Paxos算法由Leslie Lamport提出,是一种保证分布式系统一致性的算法。Paxos的工作机制主要分为三个阶段:

  1. Prepare阶段:提议者向接受者发送一个提案编号,并询问是否可以进行提议。
  2. Promise阶段:接受者收到提案编号后,如果其大于之前的提案编号,则承诺不再接受小于该编号的提案,并回复提议者。
  3. Accept阶段:提议者收到多数接受者的承诺后,发送提案内容,要求接受者接受该提案。
  4. Accepted阶段:接受者收到提案内容后,如果其编号大于或等于之前承诺的编号,则接受该提案。
2.2 Paxos Java代码示例
java">import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;class Paxos {private static class Proposal {int proposalNumber;String value;Proposal(int proposalNumber, String value) {this.proposalNumber = proposalNumber;this.value = value;}}private static class Acceptor {int promisedProposalNumber = -1;Proposal acceptedProposal = null;synchronized boolean promise(int proposalNumber) {if (proposalNumber > promisedProposalNumber) {promisedProposalNumber = proposalNumber;return true;}return false;}synchronized boolean accept(Proposal proposal) {if (proposal.proposalNumber >= promisedProposalNumber) {promisedProposalNumber = proposal.proposalNumber;acceptedProposal = proposal;return true;}return false;}synchronized Proposal getAcceptedProposal() {return acceptedProposal;}}private static class Proposer {private final Map<Integer, Acceptor> acceptors;private final int proposerId;private int proposalNumber = 0;Proposer(int id, Map<Integer, Acceptor> acceptors) {this.proposerId = id;this.acceptors = acceptors;}void propose(String value) {proposalNumber++;int n = proposalNumber * 10 + proposerId;Proposal proposal = new Proposal(n, value);int acceptedCount = 0;for (Acceptor acceptor : acceptors.values()) {if (acceptor.promise(n)) {acceptedCount++;}}if (acceptedCount > acceptors.size() / 2) {acceptedCount = 0;for (Acceptor acceptor : acceptors.values()) {if (acceptor.accept(proposal)) {acceptedCount++;}}if (acceptedCount > acceptors.size() / 2) {System.out.println("Proposal accepted: " + value);} else {System.out.println("Proposal rejected");}} else {System.out.println("Proposal rejected");}}}public static void main(String[] args) {Map<Integer, Acceptor> acceptors = new ConcurrentHashMap<>();acceptors.put(1, new Acceptor());acceptors.put(2, new Acceptor());acceptors.put(3, new Acceptor());Proposer proposer = new Proposer(1, acceptors);proposer.propose("Value1");}
}
3. Raft算法
3.1 Raft基本原理

Raft算法由Diego Ongaro和John Ousterhout提出,通过简化Paxos的概念,使一致性算法更易于理解和实现。Raft分为三个主要阶段:

  1. 选举(Leader Election):选举出一个领导者(Leader),负责管理日志复制和一致性。
  2. 日志复制(Log Replication):领导者将客户端请求按照日志条目的形式复制到其他节点上,确保日志一致性。
  3. 安全性(Safety):保证日志条目按顺序持久化,确保系统的安全性。
3.2 Raft Java代码示例
java">import java.util.*;
import java.util.concurrent.*;class Raft {enum State {FOLLOWER, CANDIDATE, LEADER}static class LogEntry {int term;String command;LogEntry(int term, String command) {this.term = term;this.command = command;}}private State state = State.FOLLOWER;private int currentTerm = 0;private int votedFor = -1;private final List<LogEntry> log = new ArrayList<>();private final Map<Integer, Raft> nodes;private int commitIndex = 0;private int lastApplied = 0;private final int id;private int voteCount = 0;Raft(int id, Map<Integer, Raft> nodes) {this.id = id;this.nodes = nodes;}synchronized void handleRequestVote(int term, int candidateId, int lastLogIndex, int lastLogTerm) {if (term > currentTerm) {currentTerm = term;state = State.FOLLOWER;votedFor = -1;}if ((votedFor == -1 || votedFor == candidateId) && term == currentTerm) {if (lastLogTerm > log.get(log.size() - 1).term || (lastLogTerm == log.get(log.size() - 1).term && lastLogIndex >= log.size() - 1)) {votedFor = candidateId;System.out.println("Node " + id + " voted for " + candidateId);}}}synchronized void startElection() {state = State.CANDIDATE;currentTerm++;votedFor = id;voteCount = 1;for (Raft node : nodes.values()) {if (node.id != id) {node.handleRequestVote(currentTerm, id, log.size() - 1, log.get(log.size() - 1).term);}}if (voteCount > nodes.size() / 2) {becomeLeader();}}synchronized void handleAppendEntries(int term, int leaderId, int prevLogIndex, int prevLogTerm, List<LogEntry> entries, int leaderCommit) {if (term >= currentTerm) {currentTerm = term;state = State.FOLLOWER;votedFor = -1;System.out.println("Node " + id + " accepted leader " + leaderId);}}synchronized void becomeLeader() {state = State.LEADER;System.out.println("Node " + id + " became leader");}public static void main(String[] args) throws InterruptedException {Map<Integer, Raft> cluster = new ConcurrentHashMap<>();for (int i = 1; i <= 3; i++) {cluster.put(i, new Raft(i, cluster));}Raft node1 = cluster.get(1);node1.startElection();TimeUnit.SECONDS.sleep(1);for (Raft node : cluster.values()) {if (node.state == State.LEADER) {System.out.println("Leader: Node " + node.id);}}}
}
4. Paxos与Raft的优缺点对比
特性PaxosRaft
易理解性比较复杂,理解困难设计简洁,易于理解
实现难度实现困难,需要处理多个状态实现相对简单
性能较高,但需要更多的消息传递性能相对较好,消息传递较少
社区支持较多,但文档和工具较少社区支持较多,文档和工具完善
应用场景适用于高容错、高可靠性的系统适用于易维护和高可用的系统
5. 总结

Paxos和Raft都是实现分布式一致性的强大工具。Paxos以其高容错性能而闻名,但其实现和理解的复杂性使得它在实际应用中较少采用。而Raft则通过简化设计,让一致性算法变得


http://www.ppmy.cn/embedded/110492.html

相关文章

【软考】安全威胁

目录 1. 说明2. 典型的安全威胁2.1 授权侵犯2.2 拒绝服务2.3 窃听2.3 信息泄露2.4 截获/修改2.5 假冒2.6 否认2.7 非法使用2.8 人员疏忽2.9 完整性破坏2.10 媒体清理2.11 物理入侵2.12 资源耗尽 3. 例题3.1 例题1 1. 说明 1.随着信息交换的激增&#xff0c;安全威胁所造成的危…

ardunio超声波测距实验

工作原理 模块有2个超声波换能器&#xff08;如图所示&#xff09;&#xff0c;一个发出声波&#xff0c;另一个接收物体反射回来的声波&#xff0c;这中间所经过的时间即声波传播的时间&#xff0c;再结合声速就能计算出&#xff1a; 距离 声速 * 时间 2 如何使用HC-SR04模块…

【lua实战】数组和数组长度

大多数编程语言中&#xff0c;一个数组很容易计算数组长度&#xff0c;一般都是使用现成的函数或者通过计算得到&#xff0c;比如&#xff1a; Python array [1, 2, 3, 4, 5] length len(array) JavaScript let array [1, 2, 3, 4, 5]; let length array.length; Java…

golang学习笔记10——golang 的 Gin 框架,快速构建高效 Web 应用

推荐学习文档 golang应用级os框架&#xff0c;欢迎star基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总golang学习笔记01——基本数据类型golang学习笔记02——gin框架及基本原理golang学习笔记03——gin框架的核心数据结构golang学…

VSCode学习笔记

1. 快捷键 KeyDescriptionPlatformF1打开命令面板&#xff08;Command Palette&#xff09;Win10Shift Delete剪切当前光标所在的代码行Win10 2. 文件 2.1 在文件列表中定位当前文件 操作路径&#xff1a;右键单击文件名 ⇒ 在右键菜单中点击 【Reveal in Explorer View】

第一篇 第3章 不确定型分析 第4章 设备更新分析 第5章价值工程

第3章 不确定型分析 预测所依据的数据不足、预测方法的局限性、外部环境等因素的变化都具有不确定性。使得经济效果评价所采用的预测值与未来的实际值之间可能出现偏差。 3.1 盈亏平衡分析 3.1.1 盈亏平衡分析的概念和分类 盈亏平衡分析是研究方案的产品利润与成本费用、产…

[基于 Vue CLI 5 + Vue 3 + Ant Design Vue 4 搭建项目] 07 如何修改 npm run serve 的启动端口号

如何修改 npm run serve 的启动端口号 首先&#xff0c;找到 npm run serve 对应的脚本 在 package.json 文件中找到 serve 对用的脚本 然后&#xff0c;添加 – port 新端口号 这里修改启动端口号为 9000&#xff0c;则在启动命令后面加上 --port 9000 最后&#xff0c;启动…

第二期: 第二节 , 逻辑编程 , gpio

1 首先就是 看原理图&#xff1a; 这里有两个 &#xff2c;&#xff25;&#xff24; 核心板的原理图。 可以看到 是这个脚。 &#xff12; 然后就是 查看数据手册。 从 数据手册可以看出 &#xff0c;一共有这么多的 gpio 组&#xff0c; 但是这些 组 是有复用的&#xf…

Elemnt-UI + 递归组件实现后台管理系统左侧菜单

Elemnt-UI 递归组件实现后台管理系统左侧菜单 在 Vue.js 中&#xff0c;允许你编写一个组件来表示一个节点&#xff0c;而这个节点可以包含多个子节点&#xff0c;每个子节点又可以是同样的组件。这种方式使得组件能够处理无限层级的嵌套结构。 应用场景 递归组件非常适合处…

数据赋能(201)——开发:数据开发管理——实施过程、应用特点

实施过程 数据开发管理的实施过程通常涉及以下几个关键步骤&#xff1a; 数据开发策划 明确数据开发目标&#xff1a; 设定数据价值开发数据开发的具体目标&#xff0c;如提高数据分析效率、优化业务决策等。设定可量化的关键绩效指标&#xff08;KPIs&#xff09;&#xff0…

鸿蒙开发EventBus

鸿蒙开发EventBus 鸿蒙没有EventBus这个库&#xff0c;有emitter这个通知库。 一、吐槽&#xff1a;虽然emitter能做EventBus功能&#xff0c;但是它存在的坑&#xff0c;真的用了才知道&#xff0c;能不用它就不用吧 坑的点&#xff1a; 一个注销&#xff0c;其他地方用到…

async/await 的理解

概念 用来实现同步的效果&#xff0c;其实就是语法糖&#xff0c;是为优化 then 链而开发出来的。 从字面上来看&#xff0c;async 是“异步”的简写&#xff0c;await 则为等待&#xff0c;所以很好理解 async 用于申明一个 function 是异步的&#xff0c;而 await 用于等待…

Redis 主从复制的原理详解

引言 Redis 作为一种高性能的内存数据库&#xff0c;广泛应用于高并发、低延迟的场景中。然而&#xff0c;单机版的 Redis 存在一定的局限性&#xff0c;尤其是在高可用性和负载均衡方面。为了应对这些挑战&#xff0c;Redis 提供了主从复制&#xff08;Replication&#xff0…

Apache SeaTunnel Committer专访刘乃杰 | 用开源推动数据同步工具的创新

作者&#xff1a;刘乃杰 编辑整理&#xff1a;曾辉 今天&#xff0c;我们有幸采访到了Apache SeaTunnel社区的新提名Committer刘乃杰&#xff0c;作为社区的活跃贡献者&#xff0c;一直为项目的发展和创新方面做着许多重要的贡献。 让我们一起走进他的开源故事&#xff0c;了…

浅谈模型在信贷营销中的应用

浅谈模型在信贷营销中的应用 当前在信贷营销场景中,用户流量竞争愈加激烈,获客成本持续攀高,客户消费观念和消费信心趋向保守,传统的信贷营销方式效果逐渐乏力,借助数据挖掘技术对用户进行多元优化及精细化管理已经成为企业在经营发展中的普遍趋势。在此背景下,本文将围…

Redis_RDB持久化

基于RDB的持久化方式会把当前内存中所有的redis键值对数据以快照的方式写入硬盘文件中&#xff0c;如果需要恢复数据&#xff0c;就把快照文件读到内存中。 RDB快照文件是经压缩的二进制格式的文件&#xff0c;它的储存路径不仅可以在redis服务器启动前通过配置参数来设置&…

类和对象(中)

片头 大家好&#xff01;在上一篇中&#xff0c;我们初步了解了类和对象&#xff0c;今天我们继续深入学习类和对象&#xff0c;准备好了吗&#xff1f;咱们开始咯&#xff01; 一、类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么…

css grid布局属性详解

Grid布局 前言一、认识Grid1.1容器和项目1.2行和列1.3单元格和网格线 二、容器属性2.1.grid-template-columns与grid-template-rows属性2.1.1 直接使用长度单位比如px2.1.2 使用百分比 %2.1.3 使用repeat函数2.1.4 按比例划分 fr 关键字2.1.5 自动填充 auto 关键字2.1.6 最大值…

JAVAWeb---JavaScript

第三章 JavaScript 一 JS简介 1.1 JS起源 Javascript是一种由Netscape(网景)的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言&#xff0c;主要目的是为了解决服务器端语言&#xff0c;遗留的速度问题&#xff0c;为客户提供更流畅的浏览效…

Java数据结构(十)——冒泡排序、快速排序

文章目录 冒泡排序算法介绍代码实现优化策略复杂度和稳定性 快速排序算法介绍优化策略非递归实现代码演示复杂度和稳定性 冒泡排序 算法介绍 冒泡排序是一种简单的排序算法。它重复地遍历要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就交换。遍历…