「分布式系列」拜占庭将军问题

简介: 拜占庭将军问题介绍

拜占庭概述


首先来说说,什么是拜占庭


网络异常,图片无法展示
|


拜占庭即拜占庭帝国,又称东罗马帝国,首都位于如今土耳其的伊斯坦布尔。如上图可以看到,当时的拜占庭帝国国土辽阔,横跨亚、欧、非大陆,环抱地中海。


拜占庭将军问题


网络异常,图片无法展示
|

在那个冷兵器时代,为了征服与反征服,在如此辽阔的疆域不同地方经常驻守着由成百上千个将军领导的军队,如上图。


  • 拜占庭帝国的军队要面临的现实问题是,如何在广阔的疆域内实现所有军队的统一调度、消息交互。在那个冷兵器时代,消息指令的传递只能依靠通信士兵人为进行传递。
  • 而因此正由于是人为传递消息,若通信士兵在传递途中因暴病、天气、地理等各种原因没有及时传递消息,再或者进行了叛变,故意不将消息传递或将统一发号的命令进行虚假篡改,那么将军们得到的指令会大相径庭,行动得不到统一保证,因此传递消息的信道是不可靠的,因此导致出现行动执行的一致性问题。


综上,便是拜占庭将军问题


拜占庭将军问题,实质上是一致性问题,是不同个体(将军)之间在分布式环境(辽阔疆域)中通过不可靠信道(通信士兵)接收消息(命令)后采取一致性行动的问题。


分布式领域中“拜占庭将军问题”


网络异常,图片无法展示
|


拜占庭将军问题,其实是一个现实存在的共性问题,只要场景和条件成立便可以认为它是某个领域的拜占庭将军问题,是一个广泛问题的现实子集而已。


我们把这个问题延伸到计算机和互联网领域的话,分布式已经是互联网中常见的服务部署方案,由于硬件错误、网络拥塞或断开以及遭到恶意攻击,计算机和网络可能出现不可预料的行为,这和拜占庭将军问题产生场景和条件是相仿的。


据此,莱斯利·兰伯特(Leslie Lamport)等提出了著名的拜占庭将军问题(Byzantine Failures) 来论述分布式环境下点对点通信时在存在消息丢失的不可靠信道上试图通过消息传递的方式达到一致性是较为困难的。


莱斯利·兰伯特(Leslie Lamport)也是著名的Paxos一致性算法的作者,关于本系列分布式介绍后续的Raft一致性算法也是在改进Paxos的初衷进行设计的,这里简单了解下概念相关性线索轨迹,感兴趣可以继续阅读本篇后续文章。


这里将现实场景的拜占庭帝国分布式服务做下对比,如下:

场景

信源

信道

消息

不可靠原因

拜占庭帝国

中央 <----> 将军
将军 <----> 将军

通信士兵

命令

天气、地理、战争、叛变

分布式服务

服务节点 <----> 服务节点

网络

报文

损毁、超时、中断、恶意攻击


问题核心剖析


问题核心


分布式环境/服务中拜占庭将军问题要解决的核心问题是:如何在缺少可信任的中央节点和可信任的通道的情况下,分布在网络中的各个节点应如何达成共识的一致性问题。 这是分布式服务中首要解决的议题,它也是其他所有问题的基础和根本保证。


问题分析


网络异常,图片无法展示
|

这里尝试剖析问题本质需要从现象到本质一层层剖析根源,这里个人认为是首先有 分布式环境 的客观现实导致服务集群的 节点分散 ,当我们充分利用和享受分布式带来的优势后也需要面临和解决海量节点个体统一协调、指挥的 一致性问题 ,而无论是让统一的控制中心下发指令还是让节点之间互相协商都必须经历 消息交互 才能实现通信彼此了解对方和外部环境的最新状态信息,而承载交互的可能的最小承载因子是 信息 ,因此我们试着从根源开始剖析!


网络异常,图片无法展示
|


根据克劳德·艾尔伍德·香农(Claude Elwood Shannon)提出的信息论,我们一般将信息分为信源、信宿、信道三部分,信息体作为载体通过信道穿梭在信源信宿之间。


下面我们基于以上概念将其引申到分布式环境下,每个服务节点在发送消息时便是信源角色,若是接收消息则是信宿角色,信道是建立在节点之前双向的通道。

网络异常,图片无法展示
|

构成

角色定位

作用

可能存在问题

信源

通信发起者

产生信息体

无法溯源、异常

信宿

通信接收者

接收信息体

伪造、异常

信道

通信通道

传递信息体

被劫持、被监听、中断

信息体

通信载体

装载信息体

篡改、丢失


因此,我们剖析该问题呈现特点是节点不可靠性通道不可靠性信息不可靠性,因此要针对这些问题特点来进行针对性解决,从而确保一致性的达成。


问题解决


解决方案:口头协议


实现过程


网络异常,图片无法展示
|

口头协议 ,又称为 口信消息(Oral message) 。满足以下三个条件的方式称为 口头协议


  • 每个被发送的消息都能够被正确的投递(信道绝对可信)
  • 信息接收者知道是谁发送的消息(消息来源可知,但不知道上一个消息来源,即不可溯源)
  • 能够知道缺少的消息


该方案的实现方式是:


  • [STEP-1] 各个节点接收Commander指令Command-1
  • [STEP-2] 每个节点收到Commander消息后,再将该消息分发到其他节点上,最终每个节点都会收到其他节点发来的消息集合{Commander消息、其他节点传递的Commander消息},根据消息集合来判断下一步如何执行。

节点

收到的消息集合

A

{ CommandCommander发送、CommandB传递、CommandC传递 }

B

{ CommandCommander发送、CommandA传递、CommandC传递 }

C

{ CommandCommander发送、CommandA传递、CommandB传递 }


不可靠场景


Commander不可靠


网络异常,图片无法展示
|

Commander 不可靠时,各节点收到信息集合为

节点

收到的消息集合

决策判断

节点执行结果

分布式一致性

A

{ command-0Commander发送、command-1B传递、command-1C传递 }

指令不一致

不执行

一致

B

{ command-1Commander发送、 command-0A传递、command-1C传递 }

指令不一致

不执行

一致

C

{ command-1Commander发送、 command-0A传递、command-1B传递 }

指令不一致

不执行

一致


Node不可靠


少数不可靠


网络异常,图片无法展示
|

少数节点不可靠场景 ,当 A 不可靠时,各节点收到信息集合为

节点

收到的消息集合

决策判断

节点执行结果

分布式一致性

A

{ command-1Commander发送、command-1B传递、command-1C传递 }

不可靠节点

执行伪造命令command-0

多数一致

B

{ command-1Commander发送、 command-0A传递、command-1C传递 }

指令不一致

不执行

多数一致

C

{ command-1Commander发送、 command-0A传递、command-1B传递 }

指令不一致

不执行

多数一致


多数不可靠


网络异常,图片无法展示
|

多数节点不可靠场景 ,当 A、C 不可靠时,各节点收到信息集合为

节点

收到的消息集合

决策判断

节点执行结果

分布式一致性

A

{ command-1Commander发送、command-1B传递、command-0C传递 }

不可靠节点

执行伪造命令command-0

多数不一致

B

{ command-1Commander发送、 command-0A传递、command-0C传递 }

指令不一致

不执行

多数不一致

C

{ command-1Commander发送、 command-0A传递、command-1B传递 }

不可靠节点

执行伪造命令command-0

多数不一致


综上,我们可以看到,无论是发号施令的Commander,还是执行命令的Node节点,当不可靠节点数量较多时,分布式中的一致性将遭受破坏,无法保证。
确保一致性情况下,不可靠节点与总结点数量关系

n代表所有节点(含Commander、Node)总数,m代表不可靠节点数量,则他们必须满足n≧3m+1这样的数量关系方可保证分布式的一致性,感兴趣可以自己推导下,以下是样例:

节点总数(n)

Commander数量(1)

Node数量(n-1)

不可靠最大节点数(m)

4

1

3

1

7

1

6

2

10

1

9

3

...

...

...

...


不足


  • 消息无法溯源,无法确定其他节点传递消息的正确性
  • 当不可靠节点大多数存在的情况下,无法达成一致性


解决方案:书面协议


为了进一步解决口头协议的不足,改进点如下:


  • 为了确保其他节点传递消息的正确性可溯源,让节点通信都增加签名信息
  • 当不可靠节点大多数存在时无法达成一致性,但这是一个未知数,无法实现按照推演公式安排节点总数量来保证容错,而消息传递加签名信息的方式能很好的确保消息传递的可溯源、准确性,以此可以解决任意不可靠节点存在的场景


实现过程


网络异常,图片无法展示
|

书面协议 ,又称为 签名协议 。该方案的实现方式是:


  • [STEP-1] 各个节点接收Commander带有签名的指令Command-1
  • [STEP-2] 每个节点收到Commander消息后,将该消息进行签名后再分发到其他节点上,最终每个节点都会收到其他节点发来的带有签名信息的消息集合{Commander签名消息、其他节点签名的Commander消息},根据消息集合来判断下一步如何执行。

节点

收到的消息集合(均带有签名信息)

A

{ CommandCommander签名、CommandB签名、CommandC签名 }

B

{ CommandCommander签名、CommandA签名、CommandC签名 }

C

{ CommandCommander签名、CommandA签名、CommandB签名 }


特点


基于口头协议的特点,签名协议规定,每条消息只可以复制,然后加上自己的签名再发给其他节点,增加如下特点:


  • [1] 节点签名是不能伪造的,内容修改可检测
  • [2] 任何节点都可以识别Commander的签名,不可靠节点可以伪造Commander的签名


不足


  • 尽管消息传递均被签名,但是发送者不一定可信,有可能是A节点使用了C节点签名来通知B节点,因此需要一个中心化的签名管理规则或机构对各个发送者进行核验方可确认真实性


书面协议的本质就是引入了签名机制,这使得所有消息都可追本溯源。化解了口头协议中1/3要求,只要采用了书面协议,可靠节点就可以达到一致。


解决方案:工作量证明


工作量证明算法(PoW算法,Proof of Work) 广泛应用于存在恶意攻击和篡改且不可靠的分布式环境下,应该广泛的是区块链技术


中本聪在比特币白皮书中,通过比特币协议给出了终极解决方案如下:


  • 引入工作量证明机制,只有第一个完成规定计算工作的节点才能传播信息,从而保证一段时间内只有一个提案。
  • 引入非对称加密算法,为信息传递提供签名技术支持,以保证消息传递的私密性,且不可抵赖、不可篡改。


关于该部分方案的实现在后续章节中单独论述。


拜占庭将军问题是由莱斯利·兰伯特(Leslie Lamport)等提出的,但真正解决这一难题的是中本聪

兰伯特提出的Paxos算法,还有后续改进的Raft算法、Zab协议都是基于可靠信道环境下消息不会被篡改和伪造等恶意攻击的前提下实现的分布式一致性协议,面对不可靠信道等存在恶意篡改风险的分布式环境是无法保证一致性的。


总结


拜占庭将军问题,不关心结果的对与错,只关心节点执行的一致性问题。

网络异常,图片无法展示
|

现有的 分布式一致性协议和算法 主要可分为两类:


  • 非拜占庭容错算法
    非容错算法(Crash Fault Tolerance, CFT),解决的是分布式系统中存在故障,但不存在恶意攻击的场景下的共识问题。也就是说,在该场景下可能存在消息丢失,消息重复,但不存在消息被篡改或伪造的场景。一般用于局域网场景下的分布式系统,如分布式数据库。属于此类的常见算法有Paxos算法、Raft算法,、Zab协议等。
  • 拜占庭容错算法
    可以解决分布式系统中既存在故障,又存在恶意攻击场景下的共识问题。一般用于互联网场景下的分布式系统,如在数字货币的区块链技术中。属于此类的常见算法有PBFT算法、PoW算法


参考


https://baike.baidu.com/item/拜占庭将军问题/265656 拜占庭问题


https://baike.baidu.com/item/信息论/302185 信息论


https://zhuanlan.zhihu.com/p/44024734


https://www.cnblogs.com/aspirant/p/13321203.html


https://www.cnblogs.com/aspirant/p/13321816.html


https://www.8btc.com/article/70370


https://www.jianshu.com/p/81aadcea109b

相关文章
|
算法 区块链
理解分布式一致性:拜占庭容错与PBFT
理解分布式一致性:拜占庭容错与PBFT
理解分布式一致性:拜占庭容错与PBFT
|
16天前
|
NoSQL Java 中间件
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
本文介绍了从单机锁到分布式锁的演变,重点探讨了使用Redis实现分布式锁的方法。分布式锁用于控制分布式系统中多个实例对共享资源的同步访问,需满足互斥性、可重入性、锁超时防死锁和锁释放正确防误删等特性。文章通过具体示例展示了如何利用Redis的`setnx`命令实现加锁,并分析了简化版分布式锁存在的问题,如锁超时和误删。为了解决这些问题,文中提出了设置锁过期时间和在解锁前验证持有锁的线程身份的优化方案。最后指出,尽管当前设计已解决部分问题,但仍存在进一步优化的空间,将在后续章节继续探讨。
454 131
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
|
19天前
|
NoSQL Java Redis
Springboot使用Redis实现分布式锁
通过这些步骤和示例,您可以系统地了解如何在Spring Boot中使用Redis实现分布式锁,并在实际项目中应用。希望这些内容对您的学习和工作有所帮助。
147 83
|
5月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
14天前
|
缓存 NoSQL 搜索推荐
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
本文介绍了如何通过Lua脚本在Redis中实现分布式锁的原子性操作,避免并发问题。首先讲解了Lua脚本的基本概念及其在Redis中的使用方法,包括通过`eval`指令执行Lua脚本和通过`script load`指令缓存脚本。接着详细展示了如何用Lua脚本实现加锁、解锁及可重入锁的功能,确保同一线程可以多次获取锁而不发生死锁。最后,通过代码示例演示了如何在实际业务中调用这些Lua脚本,确保锁操作的原子性和安全性。
43 6
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
|
27天前
|
缓存 NoSQL 中间件
Redis,分布式缓存演化之路
本文介绍了基于Redis的分布式缓存演化,探讨了分布式锁和缓存一致性问题及其解决方案。首先分析了本地缓存和分布式缓存的区别与优劣,接着深入讲解了分布式远程缓存带来的并发、缓存失效(穿透、雪崩、击穿)等问题及应对策略。文章还详细描述了如何使用Redis实现分布式锁,确保高并发场景下的数据一致性和系统稳定性。最后,通过双写模式和失效模式讨论了缓存一致性问题,并提出了多种解决方案,如引入Canal中间件等。希望这些内容能为读者在设计分布式缓存系统时提供有价值的参考。感谢您的阅读!
118 6
Redis,分布式缓存演化之路
|
3月前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
248 5
|
4月前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
121 8