分布式 | 拜占庭将军问题

简介

  • 纵所周知分布式系统存在各种的问题, 比如机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的TCP、存储数据丢失等故障行为 . 而著名的拜占庭将军问题则描述的了一个最困难的,也是最复杂的一种分布式故障场景, 因为不仅可能存在故障行为,还存在恶意行为.

拜占庭将军问题 讲的是 有多个将军(节点) 如何就某个作战计划(比如进攻或者撤退)达成共识(一致性)的问题, 将军与将军之间只能通过信使(消息)来通信, 但是在互相通信的过程中可能存在信使被杀(通讯故障或者消息丢失) , 信使被替换(消息被劫持伪造) , 将军叛变(节点被劫持发送错误信息)等问题.

1.2 二忠一叛难题

  • 忠诚的将军的行为: 发送给其他将军的消息是一致的
  • 叛变的将军的行为: 发送给其他将军的消息是不一致的或者相反的
  • 达成共识: 只需要有半数以上将军达成共识即可, 不需要全部.
  • 少数服务多数原则: 一个将军收到消息后按照少数服务多数原则进行投票表决

假设现在只有3个将军, 需要去互相通信统一作战计划达成共识, 并且A和B是忠诚的, C是叛变的情况下会存在什么样的问题?

假设A和B先发送消息, A决定进攻, B决定撤退

分布式 | 拜占庭将军问题_第1张图片
通信后A和B的投票比都是1:1, 这时如果C是忠诚的, 无论它决定进攻还是撤退, 最终A和B的票数比都是进攻[2] : 撤退[1]==》 决定进攻 或者 都是 进攻[1] : 撤退[2]==》 决定撤退, 即两者的目标是一致没问题. 但是如果C是叛变将军, 然后它给A将军发送撤退, 给B将军发送进攻的消息. 这时候问题就很大了, A收到了1个进攻两个撤退最终决定撤退, 而B收到2个进攻, 1个撤退最终决定进攻. 两者没有达成共识, 然后B单枪匹马去进攻被歼灭了.

分布式 | 拜占庭将军问题_第2张图片

1.3 二忠一叛难题的解法1: 口信消息型拜占庭问题之解

  • 通过增加额外的将军参与讨论,并且作为指挥官主导作战指令, 并且增加多轮作战讨论去解决叛变将军的信息干扰

作战规则:

  • 1、增加将军D, 大家事先约定如果没有收到消息,就执行预设的默认命令,比如"撤退".
  • 2、进行两轮作战讨论,
    • 第一轮讨论先发送作战信息的将军作为指挥官,其他的将军作为副官;指挥官先单独将他的作战信息发送给每位副官; 每位副官,将从指挥官处收到的作战信息作为他的作战指令;如果没有收到作战信息,将把默认的“撤退”作为作战指令。
    • 然后在第二轮作战讨论中, 除了第一轮的指挥官外,剩余的3位副官互相给另外2位将军发送作战信息; 然后,这3位副官按照“少数服从多数”,执行收到的作战指令。

这时候作战的讨论情况就会出现两种情况:

情况1:忠诚的将军作为指挥官
第一轮作战讨论:

  • 忠诚将军作为指挥官发送一个进攻的指令给3个副官, 理想情况下副官会把该指令作为他的作战指令
    分布式 | 拜占庭将军问题_第3张图片

第二轮作战讨论:

  • B、D、C互相给其他副官发送作战指令. B和D按照指挥官的指令发送作战指令, 这时得到的票数比都是 进攻[2] : 撤退[0], 这时候C作为叛军, 无论它发不发指令, 或者给B和D都发送撤退或者给B发送进攻给D发送撤退都好, 它都无法影响B和D的最终决定并且达成共识(都是进攻). 成功解决叛军的干扰.
    分布式 | 拜占庭将军问题_第4张图片

情况2:叛变的将军作为指挥官
第一轮作战讨论:

  • 叛军C作为指挥官给3个副官发送作战指令. 如果C不发送指令, 最终ABD都执行默认撤退指令能达成共识. 如果C都发送进攻或者撤退指令这是忠诚将军行为最终ABD也能达成共识. 最后一种情况就是发送不同的作战指令给副官. 如给AD发送撤退指令, 给B发送进攻指令.
    分布式 | 拜占庭将军问题_第5张图片

第二轮作战讨论:

  • ABD按照接收到的指挥官的指令发送作战指令, 最终都是决定撤退仍然能打成共识. 同理如果C第一轮发送两个进攻1个撤退, 最终第二轮ABD依然能达成共识决定统一进攻.

分布式 | 拜占庭将军问题_第6张图片

口信消息解决拜占庭将军问题总结:

  • 2忠1叛的共识讨论就需要增加1位将军参与讨论, 并且变成2轮作战讨论. 根据兰伯特在论文中的推导, 如果叛军人数为m,将军人数不能少于3m + 1, 作战讨论就要为m+1轮, 那么拜占庭将军问题就能解决. 这个算法前提得知道叛军人数m是多少

1.4 二忠一叛难题的解法2: 签名消息型拜占庭问题之解

  • 在不增加将军的情况下, 通过消息签名的方式解决二忠一叛的难题.
  • 通过签名机制约束叛军的叛变行为,任何叛变行为都会被发现,无论有多少忠诚的将军和多少叛军,忠诚的将军们总能达成一致的作战计划。

作战规则:

  • 忠诚将军的签名无法伪造,而且对他签名消息的内容进行任何更改都会被发现
  • 任何人都能验证将军签名的真伪。
  • 副官如果收到多条指令,将这些指令消息按照一定顺序排队排序.最终大家按照约定只执行队列的第一个或中间一个或最后一个作为自己的作战命令.

这时候作战的讨论情况也会出现两种情况:

情况1:忠诚的将军作为指挥官
第一轮作战讨论:

  • A作为指挥官对消息进行签名任何人对消息的串改都会被发现, 然后发送进攻消息给BC副官,
    分布式 | 拜占庭将军问题_第7张图片

第二轮作战讨论:

  • B遵从指挥官的指令发送进攻作战指令给C, 如果C不发送指令给B, 最终B执行A的指令, AB能达成共识. 而如果C修改或伪造收到的A的作战信息给B, 则会被B发现并忽略. AB依然能达成共识. 即都是进攻

情况2:叛变的将军作为指挥官
第一轮作战讨论:

  • C作为指挥官给A和B发送不一致的消息
    分布式 | 拜占庭将军问题_第8张图片

第二轮作战讨论:

  • B队列的指令是[撤退,进攻]、A队列里的指令是[撤退、进攻]. 取最后一个作为指令两者仍然能达成共识
    分布式 | 拜占庭将军问题_第9张图片

签名消息型解决拜占庭将军问题总结:

  • 无论有多少忠诚的将军和多少叛将,忠诚的将军们总能达成一致的作战计划
  • 如果m个叛将,仍需要 执行m+1轮作战讨论
  • 它解决的是忠将们如何就作战计划达成共识的问题, 不在乎结果到底是进攻还是撤退,在乎的是忠将会不会达成一致而已.

总结

  • 故障行为: 丢失消息,或者有消息重复
  • 恶意行为: 节点被劫持攻击发送错误消息, 伪造消息

拜占庭将军问题描述的是分布式系统的除了存在故障行为,还存在恶意行为的场景. 解决拜占庭将军问题的算法叫 拜占庭容错算法(Byzantine Fault Tolerance,BFT), 比如PBFT算法,PoW算法

非拜占庭容错算法 , 又名故障容错算法(Crash Fault Tolerance,CFT) , 它解决的是 分布式系统存在 故障行为 但不存在 恶意行为的共识问题, 常见的算法有Paxos算法、Raft算法、ZAB协议

10、打赏

如果觉得文章有用,你可鼓励下作者(支付宝)

在这里插入图片描述

你可能感兴趣的:(分布式微服务,分布式,算法)