分布式 — 拜占庭将军问题

拜占庭问题是用来描述 分布式系统一致性问题 在论文中抽象出来的一个著名的例子。

拜占庭帝国派出多支军队去围攻一支敌军,每支军队有一个将军,但由于彼此距离较远,他们之间只能通过信使传递消息。敌方很强大,固而必须有超过半数的拜占庭军队一同参与进攻才可能击败敌人。在此期间,将军们彼此之间需要通过信使传递消息并协商一致后,在同一时间点发动进攻。

一、三将军的难题

首先,将问题简化成只有三个将军,分别为 A、B、C,他们要讨论的只有一件事情:明天是进攻还是撤退。为此,将军们需要依据「少数服从多数」原则投票表决,只要有两个人意见达成一致就可以了。

举例来说,A 和 B 投进攻,C 投撤退:

  1. A 传递给 B 和 C 的消息都是进攻;
  2. B 传递给 A 和 C 的消息都是进攻;
  3. C 传递给 A 和 B 的消息都是撤退。

这样三个将军就都知道进攻方和撤退方二者占比是 2: 1了,进攻方胜出,第二天大家都要进攻,三者行动最终达成一致。

二忠一叛

但是如果三个将军中出现了一个叛徒(恶意节点),叛徒的目的是破坏忠诚将军间一致性的达成,让拜占庭的军队遭受损失。

假设 A 和 B 是忠诚将军,A 投进攻,B 投撤退,而 C 这个叛徒将军,不按常规出牌,传递给 A 的消息是进攻,而传递给 B 的消息是撤退。

第二天,忠诚的 A 就去进攻了,而同样忠诚的 B 却撤退了,最终,A 的军队就会败给敌人。

可以看出,在一致性达成的过程中,叛徒将军(恶意节点)甚至不需要超过半数,就可以破坏占据多数的正常节点一致性的达成。

二、解决方案

1. 口信消息型

首先假设:

  1. 每个被发送的消息都能够被正确的投递;
  2. 信息接收者知道是谁发送的消息;
  3. 能够知道缺少的消息(如果叛军不配合发送消息,算法默认一个值「撤退」的来替代)

如果叛将人数为 m,将军人数不能少于 3m + 1,那么拜占庭将军问题就能解决了。

以 4 位将军为例,需要进行两轮作战信息协商:

  • 第一轮
    • 先发送作战信息的将军作为指挥官,其他的将军作为副官;
    • 指挥官将他的作战信息发送给每位副官;
    • 每位副官,将从指挥官处收到的作战消息,作为他的作战指令;如果没有收到作战信息,将把默认的「撤退」作为作战指令。
  • 第二轮
    • 除了第一轮的指挥官外,剩余的 3 位将军将分别作为指挥官,向另外 2 为将军发送作战信息;
    • 这 3 位将军按照「少数服从多数」,执行收到的作战指令。

在二忠一叛的问题中,在存在 1 位叛将的情况下,必须增加 1 位将军,将 3 位将军协商共识,转换为 4 位将军协商共识,这样才能实现忠诚将军的一致性作战计划。

2. 签名消息型

签名消息型在口信消息定义的基础上增加了两条:

  1. 忠诚将军的签名无法伪造,而且对他签名消息的内容进行任何更改都会被发现;
  2. 任何人都能验证将军签名的真伪。

忠将 A 率先给 B 和 C 发起进攻命令,若叛将 B 修改或伪造收到的作战信息,那么在忠将 C 收到 A 的作战信息被修改,B 已叛变,这时 B 执行 A 发送的作战信息。