Der Blog

Erstellen skalierbarer verteilter Systeme

von Cees de Groot 7. Juni 2017 | 9 Minuten Lesezeit

Vorbeugen ist die beste Medizin

Der beste Weg, ein verteiltes System aufzubauen, besteht darin, es zu vermeiden. Der Grund ist einfach: Sie können die Irrtümer des verteilten Rechnens (die meisten davon gelten entgegen der Ansicht einiger Optimisten immer noch) und arbeiten mit den schnellen Bits eines Computers.

Mein persönlicher Laptop hat einen schönen Aufkleber von SignalFX ; es handelt sich um eine Liste der Geschwindigkeiten verschiedener Transportmechanismen. Der Aufkleber weist grundsätzlich darauf hin, Festplatten und Netzwerke zu vermeiden, insbesondere wenn man zwischen Rechenzentren wechselt. Wenn Sie das tun und einige mechanische Sympathie , können Sie tolle Sachen bauen wie der LMAX-Disruptor Das kann eine Handelsplattform unterstützen, die Millionen von Transaktionen pro Sekunde auf einem einzigen Knoten ausführen kann. Wenn Sie Daten im Speicher und auf einer einzigen Maschine speichern, können Sie viel erreichen. Wenn Sie damit einverstanden sind, bei einem Fehler vielleicht 15 Sekunden Arbeit zu wiederholen, können Sie die gesamte Arbeit im Speicher erledigen und nur viermal pro Minute Checkpoints auf die Festplatte schreiben. Solche Systeme laufen extrem schnell, und Sie können die Frage der Skalierung komplett umgehen.

Lassen Sie sich nicht täuschen – verteilte Systeme erhöhen immer die Komplexität und verringern die Produktivität. Wer Ihnen etwas anderes erzählt, verkauft Ihnen wahrscheinlich Quacksalberei.

Wissen Sie, warum Sie es tun – und hinterfragen Sie alle Annahmen

Es gibt die Anforderung „Hochverfügbarkeit“, die es unmöglich macht, den gesamten Code auf einem Knoten zu platzieren. Diese Anforderung führt oft zu dem sehr kostspieligen Schritt, mehrere Systeme zu integrieren. Hier gilt es, Annahmen und Anforderungen zu hinterfragen. Muss dieses System wirklich eine Verfügbarkeit von 99,9999 % aufweisen oder können wir es auf eine niedrigere Verfügbarkeitsstufe verschieben? Gerade wenn sich Ihre Software noch beweisen muss, kann Hochverfügbarkeit und anderer Schnickschnack eine verfrühte Optimierung sein. Verzichten Sie stattdessen vorerst darauf, bringen Sie die Software schneller auf den Markt und entwickeln Sie eine Strategie für spätere Implementierungen. Wenn die Stakeholder des Unternehmens „Hochverfügbarkeit“ befürworten, erläutern Sie die Kompromisse und stellen Sie sicher, dass sie wissen, dass sie Zeit und Geld in etwas investieren, das sie möglicherweise nie nutzen werden (das erwartete Ergebnis sollte sein, dass Kunden das Produkt oder die Funktion nicht mögen werden). Wenn Sie nur Produkte oder Funktionen entwickeln, von denen Sie wissen, dass sie den Kunden gefallen werden, gehen Sie kein Risiko ein, und Ihr Projekt endet in einer langweiligen Mittelmäßigkeit).

Erklären das CAP-Theorem , sagen Sie Ihren Stakeholdern, dass sie entweder Verfügbarkeit oder Konsistenz haben können, aber nicht beides (auch hier sagen einige Optimisten, dass dies nicht mehr der Fall ist, aber ich halte das für falsch). Wenn Sie beispielsweise ein System bauen, das beispielsweise Benachrichtigungen übermittelt, können Ihre Stakeholder ein System erhalten, das in den meisten Fällen genau einmal eine Benachrichtigung übermittelt (konsistent, aber weniger verfügbar) oder ein System, das fast immer mindestens einmal eine Benachrichtigung übermittelt (verfügbar, aber weniger konsistent). Normalerweise benötigen letztendlich konsistente (AP-)Systeme weniger Koordination und sind daher einfacher zu erstellen und zu skalieren und zu betreiben. Versuchen Sie herauszufinden, ob Sie damit durchkommen, denn es lohnt sich in der Regel, Ihr Problem in eine AP-Lösung umzudefinieren.

Denken Sie daran: Wenn es sich nicht vermeiden lässt, reduzieren Sie es zumindest auf ein einfaches Niveau. Die beste Lösung für ein verteiltes System besteht darin, kein komplexes verteiltes System implementieren zu müssen.

Machen Sie Ihr Leben einfacher

Komplexität ist der Feind unseres Fachs. Egal, welches System Sie entwerfen oder welchen Code Sie schreiben, Sie müssen dieses „Whack-a-Mole“-Spiel spielen, bei dem Komplexität auftaucht und sofort wieder zunichtegemacht wird. Dies wird umso wichtiger, sobald Sie Software schreiben, die mehr als ein System umfasst – verteilte Systeme sind von Natur aus komplex, daher sollten Sie keine Geduld haben mit zufällige Komplexität . Einige Dinge in verteilten Systemen sind einfacher zu implementieren als andere – versuchen Sie, sich auf die einfachen Dinge zu beschränken.

Verteilen für HA

Es gibt verschiedene Möglichkeiten, die Verfügbarkeit zu erhöhen. Sie können einen Knotencluster erstellen und alles koordinieren (den Arbeitsstatus ständig speichern, damit jeder Knoten alles abrufen kann). Das erfordert jedoch viel Koordination. Koordination macht Dinge anfällig, also ist sie vielleicht nicht möglich? Es gibt verschiedene Möglichkeiten, Koordination zu vermeiden und dennoch eine gute Verfügbarkeit zu gewährleisten:

  • Führen Sie die gleiche Arbeit parallel aus, verwenden Sie jedoch die Ergebnisse nur eines Systems. Alle Daten werden auf einem sekundären Knoten repliziert. Bei Ausfall des primären Knotens stellt die Replikation sicher, dass der Backup-Knoten einsatzbereit ist und sofort übernehmen kann. Die Koordination besteht dann lediglich darin, zu entscheiden, welcher Knoten zuerst ausgeführt wird und welcher als sekundäres Backup fungiert.
  • Halten Sie einen Ersatz-Standby bereit. Der primäre Knoten speichert regelmäßig die Arbeit auf einem gemeinsam genutzten Speicher. Wenn dieser ausfällt, liest der sekundäre Knoten diesen aus und übernimmt. Die Koordination besteht hier normalerweise darin, dass der sekundäre Knoten den primären Knoten im Auge behält, um zu sehen, ob eine Übernahme erforderlich ist.

In beiden Fällen verschiebt sich die Koordination von „pro Transaktion“ zu „pro Konfiguration“. Verteilte Arbeitstransaktionen sind schwierig, daher sollten Sie, wenn möglich, mit Koordination auf Konfigurationsebene arbeiten. Oftmals ist damit die Wiederholung von Arbeitsvorgängen verbunden – ein „exakt einmaliger“ Arbeitsvorgang wird zu „fast immer genau einmal, außer eine Maschine fällt aus, und dann wiederholen wir die letzte Minute, um sicherzustellen, dass wir nichts übersehen“. Die Modellierung von Operationen als idempotente Methode hilft; manchmal lässt sich das Auftauchen doppelter Operationen nicht vermeiden, und Sie müssen mit den Stakeholdern über die Anforderungen sprechen. Führen Sie eine ehrliche Risikobewertung durch (wie oft pro Jahr fallen Maschinen einfach aus?), eine ehrliche Folgenabschätzung (wie viele Aufgaben werden doppelt erledigt und welche Unannehmlichkeiten entstehen dadurch für die Benutzer) und eine ehrliche Schwierigkeitsbewertung (Mehrarbeit, höhere Komplexität, die zu mehr Sprödigkeit führt, was wiederum zu weniger Verfügbarkeit).

Manchmal ist Verfügbarkeit auch bei Rechenzentrumsausfällen erforderlich. Seien Sie in diesem Fall besonders vorsichtig, denn die Situation wird schnell brüchig. Stellen Sie sicher, dass der Koordinationsaufwand minimal ist.

Leistungsorientierte Verteilung

Manchmal lässt sich nicht die gesamte Arbeit auf einem einzigen Knoten erledigen. Versuchen Sie zunächst, nicht in diese Situation zu geraten. Schauen Sie sich die Hintergründe an und sehen Sie, wo Sie Zyklen verschwenden – diese lästigen LMAX-Leute haben gezeigt, dass man auf einer einzigen Maschine siebenstellige Transaktionen pro Sekunde durchführen kann; vielleicht sollten Sie sich für die größere Instanz an Amazon wenden. Mittlerweile würde ich erwarten, dass gute Software Multi-Core-fähig ist, sodass Sie tatsächlich schnell mit leistungsstärkerer Hardware Abhilfe schaffen können. Wenn Sie Ihren Code nicht so organisieren können, dass er mit mehr Kernen schneller läuft, haben Sie wahrscheinlich keine Chance, ihn durch das Hinzufügen weiterer Knoten zu beschleunigen, oder? Selbst ohne LMAX-Entwicklung halte ich es für vernünftig, von Ihrer Software zu erwarten, dass sie mindestens einen niedrigen fünfstelligen Geschäftswert pro Sekunde verarbeitet. Wenn Sie skalieren möchten, weil ein Knoten mehrere Hundert Transaktionen pro Sekunde nicht bewältigen kann, sollten Sie vielleicht zunächst noch einmal von vorne beginnen. Höchstwahrscheinlich gibt es Probleme in Ihrem Code, die behoben werden müssen.

Wenn Sie zur Lösung des Problems weitere Maschinen hinzufügen müssen (das ist ein großartiges Problem!), planen Sie es so, dass die Koordination minimal ist.

  • Nutzen Sie Konfigurationskoordination statt Transaktionskoordination. Lassen Sie Ihre Knoten die Arbeit mithilfe eines Koordinationsschemas aufteilen und den Prozess dann in ihren eigenen Blöcken ausführen, ohne dass eine weitere Koordination erforderlich ist. Sie können hier ganz einfach einen HA-Aspekt hinzufügen, indem Sie die Knoten die Arbeit neu verteilen lassen, wenn einer nicht verfügbar ist.
  • Versuchen Sie, die peinlich parallelen Arbeitsschritte zu finden, sodass Sie überhaupt keine Koordination benötigen. Zustandslose Webserver sind hier ein gutes Beispiel, aber das ist nicht der einzige Ort, an dem Sie ein Problem einfach mit einer unkoordinierten Gruppe von Knoten lösen können.

Speicherplatz ist günstig, nutzen Sie ihn

Architekturmuster wie Befehls-/Abfragetrennung Und Ereignisbeschaffung Entkoppeln und duplizieren Sie die Datenspeicherung häufig in mehrere spezialisierte Phasen. Diese spezialisierten Phasen eignen sich gut für verteilte Designs, da Sie wählen können, was lokal gespeichert und was verteilt werden soll. So erhalten Sie eine Hybridlösung mit minimalem Koordinationsaufwand. Beispielsweise können Sie Update-Befehle in eine verteilte Kafka Cluster, aber alles, was von dort nachgelagert ist, muss lokal und getrennt betrieben werden (z. B. verarbeiten Verbraucher die Update-Befehle und aktualisieren unabhängig ElasticSearch Knoten, die für Abfragen verwendet werden). Die „echten“ Daten sind hochverfügbar und in Nachrichtenströmen koordiniert – Systeme verwenden lediglich Ansichten dieser Daten für spezielle Verarbeitungen wie Suche, Analyse usw. Ein solches System ist deutlich einfacher zu warten als die klassische Konfiguration, bei der ein zentrales Datenbanksystem den Knotenpunkt aller Vorgänge bildet und unweigerlich zum Engpass wird – unabhängig davon, ob das Datenbanksystem auf Skalierbarkeit ausgelegt ist oder nicht.

Speichern Sie Daten redundant und nutzen Sie mehrere unabhängige Systeme, die jeweils ihre eigene optimierte Form der Daten verwenden. Dies reduziert den Koordinationsaufwand und gleicht letztendlich die relativ geringen Mehrkosten für die Speicherung aus.

Vergessen Sie das NIH-Syndrom – Ihr Rad wurde bereits anderswo neu erfunden

Sofern Sie nicht im Maßstab von Google arbeiten, ist das System, das Sie in den Bereich der verteilten Systeme einführen, nicht so speziell, dass Sie es von Grund auf neu entwickeln müssten. Sie werden wahrscheinlich dafür bezahlt, Geschäftsprobleme zu lösen, nicht dafür, Tools und Infrastruktur zu entwickeln. Es gibt also keinen Grund, sich 2017 selbst darum zu kümmern. Die korrekte Implementierung eines verteilten Systems ist schwierig, daher werden Sie wahrscheinlich Fehler machen (dasselbe gilt übrigens auch für Persistenz und Kryptografie). Wenn Sie glauben, ein einzigartiges Problem zu haben und Ihr eigenes Problem lösen zu müssen, haben Sie entweder nicht gründlich genug recherchiert oder sich nicht ausreichend bemüht, Ihr Problem so zu formulieren, dass die Nutzung eines der Hunderten von Open-Source-Projekten möglich ist. Sie haben das Unternehmen dazu gedrängt, die Anforderungen so zu formulieren, dass eine verteilte Lösung deutlich einfacher (und damit zuverlässiger) wird. Jetzt müssen Sie sich selbst anstrengen, die richtige Software zu finden, die die nicht einzigartigen Aspekte Ihres Problems löst, damit Sie sich auf das konzentrieren können, was Ihr Unternehmen ausmacht.

Ja, Werkzeugschmieden macht Spaß – ich liebe es und könnte den ganzen Tag damit verbringen. Und tatsächlich ist es gut für das Selbstwertgefühl, sein Problem so zu formulieren, dass man wie eine einzigartige Schneeflocke aussieht. Schluss damit und löse echte Probleme – die Art, die dein Unternehmen erfolgreich macht.