Im Kern der modernen Cloud-Computing- und Blockchain-Technologien gibt es Konsensalgorithmen (consensus algorithms), die den Zustand über mehrere Computer (Knoten) hinweg teilen und synchronisieren. In diesem Artikel werden wir von den theoretischen Grundlagen des “Problems der byzantinischen Generäle” (Byzantine Generals Problem) ausgehen und uns eingehend mit Paxos und Raft, die in praktischen Systemen weit verbreitet sind, sowie mit BFT (Byzantine Fault Tolerance) in Umgebungen mit böswilligen Teilnehmern befassen, einschließlich mathematischer Beweise und Code-Implementierungen.
1. Konsensbildung und Herausforderungen in verteilten Systemen
In verteilten Systemen treten verschiedene Fehler auf, die auf einem einzelnen Computer nicht möglich sind, wie Netzwerkverzögerungen, Paketverluste, Knotenabstürze oder sogar böswillige Manipulationen. Der Konsensalgorithmus ist ein Mechanismus, um trotz dieser Fehler einen konsistenten Zustand (State) im gesamten System aufrechtzuerhalten.
Die Fehlertoleranz des Systems wird hauptsächlich in die folgenden zwei Kategorien eingeteilt:
- CFT (Crash Fault Tolerance) : Kann das Stoppen (Abstürzen) von Knoten und Netzwerkpartitionierungen tolerieren, geht aber nicht von Knoten aus, die gefälschte Daten senden (böswilliges Verhalten).
- BFT (Byzantine Fault Tolerance) : Kann nicht nur Knotenabstürze tolerieren, sondern auch Situationen, in denen böswillige Knoten beliebige ungültige Nachrichten senden.
Das berühmte Problem der byzantinischen Generäle hat dieses BFT-Konzept hervorgebracht.
2. Problem der byzantinischen Generäle (Byzantine Generals Problem)
Das 1982 von Leslie Lamport, Robert Shostak und Marshall Pease vorgeschlagene “Problem der byzantinischen Generäle” modelliert, wie man unter korrekten Teilnehmern in einem Netzwerk mit böswilligen Teilnehmern einen Konsens erzielt.
2.1 Definition des Problems
Generäle des byzantinischen Reiches belagern eine feindliche Stadt. Sie sind geografisch getrennt und können nur durch Boten kommunizieren. Die Generäle müssen sich auf einen Aktionsplan einigen: entweder “Angriff” oder “Rückzug”. Unter den Generälen befinden sich jedoch Verräter (böswillige Knoten), die falsche Nachrichten senden können, um die anderen Generäle zu verwirren.
Die Bedingungen, die loyale Generäle erfüllen müssen, sind:
- Alle loyalen Generäle müssen sich auf denselben Aktionsplan (Angriff oder Rückzug) einigen.
- Eine kleine Anzahl von Verrätern darf loyale Generäle nicht dazu bringen, einen falschen (oder inkonsistenten) Konsens zu erzielen.
2.2 Mathematische Formulierung und Unmöglichkeit
Sei $ n $ die Gesamtzahl der Generäle und $ f $ die Anzahl der Verräter. Lamport und seine Kollegen bewiesen mathematisch, dass in Fällen, in denen Nachrichten manipuliert werden können (unsignierte Nachrichten), ein Konsens unmöglich ist, es sei denn, die folgende Bedingung ist erfüllt.
$ n > 3f $
Das heißt, die Gesamtzahl der Knoten muss mehr als das Dreifache der Anzahl der Verräter betragen. Umgekehrt kann das System keinen sicheren Konsens erreichen, wenn $ 1/3 $ oder mehr aller Knoten böswillig sind.
Betrachten wir als Beispiel den Fall von $ n = 3 $ und $ f = 1 $. Angenommen, es gibt die Generäle A (Kommandant), B und C, und A ist ein Verräter. A sagt B “Angriff” und C “Rückzug”. B und C tauschen die von A empfangenen Nachrichten miteinander aus, aber B behauptet “A sagte Angriff” und C behauptet “A sagte Rückzug”. Zu diesem Zeitpunkt wird es für B und C unmöglich festzustellen, ob die andere Partei lügt oder ob A lügt.
Das Folgende ist ein Mermaid-Diagramm, das diesen unmöglichen Fall für $ n = 3 $ veranschaulicht.
graph TD
A("Kommandant A<br/>Verräter") -- "Angriff" --> B("General B<br/>Loyal")
A -- "Rückzug" --> C("General C<br/>Loyal")
B -- "A sagte Angriff" --> C
C -- "A sagte Rückzug" --> B
style A fill:#ff9999,stroke:#ff0000,stroke-width:2px
3. Paxos: Ein Meilenstein des theoretischen Konsenses
Im Bereich von CFT (Crash Fault Tolerance), der byzantinische Fehler nicht berücksichtigt, war Paxos der erste leistungsstarke Algorithmus. Er wurde ebenfalls 1989 von Leslie Lamport vorgeschlagen (veröffentlicht 1998) und wird in Googles Chubby und Spanner verwendet.
3.1 Rolle und Phasen von Paxos
Paxos besteht aus mehreren Proposern (Vorschlagenden), Acceptors (Akzeptierenden) und Learnern (Lernenden). Das grundlegende Paxos (Single-Decree Paxos) ist ein Prozess zur Einigung auf einen einzigen Wert und ist in die folgenden zwei Phasen unterteilt.
- Phase 1: Prepare (Vorbereitung)
- Der Proposer wählt eine eindeutige Vorschlagsnummer $ n $ und sendet eine
Prepare(n)-Anfrage an die Mehrheit der Acceptors. - Wenn $ n $ größer ist als die Nummer einer zuvor empfangenen
Prepare-Anfrage, verspricht der Acceptor, keine Vorschläge mehr zu akzeptieren, die kleiner als $ n $ sind, und gibt alle zuvor akzeptierten Werte zurück, falls vorhanden.
- Der Proposer wählt eine eindeutige Vorschlagsnummer $ n $ und sendet eine
- Phase 2: Accept (Akzeptanz)
- Sobald der Proposer Antworten von einer Mehrheit der Acceptors erhält, sendet er eine
Accept(n, v)-Anfrage. Hier ist $ v $ der Wert mit der höchsten Vorschlagsnummer unter den in den Antworten enthaltenen Werten, oder der Wert, den er selbst vorschlagen möchte, wenn kein solcher existiert. - Der Acceptor akzeptiert den Vorschlag, sofern er kein Versprechen für eine höhere Nummer abgegeben hat.
- Sobald der Proposer Antworten von einer Mehrheit der Acceptors erhält, sendet er eine
3.2 Paxos-Simulation in Python
Der folgende Python-Code simuliert auf vereinfachte Weise das Verhalten der Phasen 1 und 2 von Paxos.
| |
4. Raft: Ein auf Verständlichkeit ausgelegter Algorithmus
Während Paxos sehr mächtig ist, ist sein Algorithmus komplex und schwierig in realen Systemen zu implementieren. Daher wurde Raft 2014 von Diego Ongaro und John Ousterhout mit dem Schwerpunkt auf “Verständlichkeit” (Understandability) entworfen. Heute ist es in etcd, Consul und anderen weit verbreitet.
4.1 Schlüsselkonzepte von Raft
Raft unterteilt den Gesamtsystemzustand in zwei Teilprobleme: Wahl des Anführers (Leader Election) und Log-Replikation (Log Replication).
Ein Knoten befindet sich immer in einem der folgenden drei Zustände:
- Leader (Anführer) : Empfängt Anfragen von Clients und repliziert Logs auf andere Knoten.
- Follower (Anhänger) : Befolgt Anfragen vom Leader.
- Candidate (Kandidat) : Zustand der Kandidatur als neuer Leader, wenn der aktuelle Leader ausfällt.
stateDiagram-v2
[*] --> Follower
Follower --> Candidate : "Timeout aufgetreten"
Candidate --> Candidate : "Wahl-Timeout"
Candidate --> Leader : "Mehrheit der Stimmen erhalten"
Candidate --> Follower : "Neuen Leader entdeckt"
Leader --> Follower : "Höheren Term entdeckt"
4.2 Mechanismus der Leader-Wahl
Raft verwendet eine logische Uhr namens Term (Amtszeit). Jeder Follower hat ein zufälliges Wahl-Timeout (Election Timeout). Wenn der Heartbeat vom Leader stoppt und ein Timeout auftritt, wird er zu einem Candidate und fordert Stimmen für sich an (RequestVote). Der Knoten, der die Mehrheit der Stimmen erhält, wird der neue Leader. Die zufällige Gestaltung der Timeouts verhindert eine Stimmenaufteilung (Split Vote).
4.3 Typdefinition des Raft-Knotenzustands in Haskell
Die Modellierung von Raft-Zustandsübergängen mit einer funktionalen Sprache macht deren Robustheit klarer. Nachfolgend finden Sie ein Beispiel für eine vereinfachte Typdefinition in Haskell.
| |
Durch die Beschreibung von Zustandsübergängen als reine Funktionen auf diese Weise wird es einfacher, die Korrektheit der Raft-Logik zu verifizieren.
5. Praktische byzantinische Fehlertoleranz: PBFT
Paxos und Raft sind CFT (absturztolerant), aber sie sind machtlos, wenn böswillige Knoten im Netzwerk existieren. Die Lösung für dieses Problem (das Problem der byzantinischen Generäle) mit praktischer Leistung war PBFT (Practical Byzantine Fault Tolerance), das 1999 von Miguel Castro und Barbara Liskov eingeführt wurde.
5.1 Kommunikationsphasen in PBFT
In PBFT gibt es einen Leader (Primary) und Follower (Backup), und die folgende 3-Phasen-Multicast-Kommunikation wird für Client-Anfragen durchgeführt.
- Pre-prepare : Der Primary weist der Anfrage eine Sequenznummer zu und sendet sie an alle Knoten.
- Prepare : Jeder Knoten empfängt die Anfrage, verifiziert sie und sendet dann eine
Prepare-Nachricht an alle anderen Knoten. Nach Erhalt von $ 2f $Prepare-Nachrichten tritt der Knoten in den Prepared-Zustand ein. - Commit : Ein Knoten im Prepared-Zustand sendet eine
Commit-Nachricht an alle Knoten. Nach Erhalt von $ 2f + 1 $Commit-Nachrichten ist der Konsens abgeschlossen und die Anfrage wird ausgeführt.
sequenceDiagram
participant C as "Client"
participant P as "Primary"
participant B1 as "Backup 1"
participant B2 as "Backup 2"
participant B3 as "Backup 3 (Böswillig)"
C->>P: "Request"
P->>B1: "Pre-prepare"
P->>B2: "Pre-prepare"
P->>B3: "Pre-prepare"
Note over P,B3: "Prepare-Phase (O(N^2) Kommunikation)"
B1->>P: "Prepare"
B1->>B2: "Prepare"
B2->>P: "Prepare"
B2->>B1: "Prepare"
Note over P,B3: "Commit-Phase (O(N^2) Kommunikation)"
P->>B1: "Commit"
B1->>B2: "Commit"
B2->>P: "Commit"
P->>C: "Reply"
B1->>C: "Reply"
B2->>C: "Reply"
PBFT arbeitet mit einer Knotenkonfiguration von $ n = 3f + 1 $, die die zuvor erwähnte Bedingung $ n > 3f $ erfüllt. Es beinhaltet einen Kommunikations-Overhead von $ O(N^2) $ zwischen den Knoten, bietet jedoch eine endgültige Übereinkunft (Finality). Dies wird in modernen Konsortium-Blockchains (wie Hyperledger Fabric) weit verbreitet verwendet.
5.2 Erneute Überprüfung der mathematischen Einschränkungen
Damit PBFT die Sicherheit aufrechterhalten kann, wird davon ausgegangen, dass die im System ausgetauschten Nachrichten kryptografisch sicher (nicht fälschbar) sind. Wenn $ Q $ die Größe des Quorums ist, müssen die folgenden Bedingungen erfüllt sein.
$ Q = 2f + 1 \\\\ n = 3f + 1 $
Die Schnittmenge von zwei beliebigen Quoren $ Q_1 $ und $ Q_2 $ muss immer mindestens einen korrekten Knoten enthalten.
$ |Q_1 \cap Q_2| = 2Q - n = 2(2f + 1) - (3f + 1) = f + 1 $
Auf diese Weise wird die Konsistenz des gesamten Systems bewiesen, denn selbst wenn $ f $ böswillige Knoten zu beiden Quoren gehören, ist immer mindestens ein ehrlicher Knoten enthalten.
6. Zusammenfassung: Die Evolution von Konsensalgorithmen
In diesem Artikel haben wir die Konsensbildung, die größte Herausforderung in verteilten Systemen, von dem theoretischen “Problem der byzantinischen Generäle” über das absturztolerante Paxos und Raft bis hin zu PBFT, das resistent gegen böswillige Knoten ist, erklärt.
- Paxos : Eine mathematisch bewiesene, robuste Grundlage, aber die Komplexität ist ein Problem.
- Raft : Legt den Fokus auf Verständlichkeit und einfache Implementierung und ist zum De-facto-Standard für moderne verteilte KVS geworden.
- PBFT : Erreicht einen deterministischen Konsens in Umgebungen mit böswilligen Knoten und wurde zur Grundlage der Blockchain-Technologie.
Heute entstehen ständig neue BFT-Algorithmen, wie der in Bitcoin verwendete Nakamoto Consensus (PoW), Tendermint und HotStuff, die den Kommunikations-Overhead von PBFT reduzieren und die Skalierbarkeit verbessern. Die Wahl des richtigen Konsensalgorithmus in Abhängigkeit von den Systemanforderungen (Knotenzuverlässigkeit, erforderlicher Durchsatz, Latenz) ist der Schlüssel zum Aufbau eines robusten verteilten Systems.
