Der Blog

Aufbau skalierbarer verteilter Systeme

von Cees de Groot 7. Juni 2017 | 9 min lesen

Vorbeugen ist die beste Medizin

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

Mein persönlicher Laptop hat einen schönen Aufkleber von SignalFX ; es ist eine Liste der Geschwindigkeiten verschiedener Transportmechanismen. Im Grunde sagt der Aufkleber, man solle Festplatten und Netzwerke meiden, besonders wenn man zwischen Rechenzentren hin- und hergeht. Wenn man das tut und einige mechanische Sympathie , können Sie tolle Sachen bauen wie der LMAX-Disruptor das eine Handelsplattform unterstützen kann, die Millionen von Transaktionen pro Sekunde auf einem einzigen Knoten ausführen kann. Wenn Sie die Dinge im Speicher und auf einer einzigen Maschine behalten, können Sie viel erreichen; wenn es Ihnen nichts ausmacht, bei einem Fehler vielleicht 15 Sekunden Arbeit zu wiederholen, können Sie die gesamte Arbeit im Speicher erledigen und nur viermal pro Minute Prüfpunkte auf die Festplatte schreiben. Systeme wie dieses laufen extrem schnell und Sie können die Frage der Skalierung vollständig umgehen.

Lassen Sie sich nicht täuschen – verteilte Systeme erhöhen immer die Komplexität und verringern die Produktivität. Wenn Ihnen jemand etwas anderes erzählt, dann ist das wahrscheinlich ein Schwindel.

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

Es gibt diese Anforderung namens „hochverfügbar“, die es unmöglich macht, den gesamten Code auf einem Knoten zu platzieren. Diese Anforderung führt häufig zu dem sehr kostspieligen Schritt, mehrere Systeme einzubeziehen. Hier gibt es zwei Dinge zu tun: Annahmen und Anforderungen in Frage stellen. Muss dieses bestimmte System wirklich eine Verfügbarkeit von 99,9 ...

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 denke, das ist falsch). Wenn Sie beispielsweise ein System bauen, das beispielsweise Benachrichtigungen liefert, können sie ein System erhalten, das die meiste Zeit genau einmal eine Benachrichtigung liefert (konsistent, aber weniger verfügbar) oder ein System, das fast immer mindestens einmal eine Benachrichtigung liefert (verfügbar, aber weniger konsistent). Normalerweise benötigen letztendlich konsistente (AP) Systeme weniger Koordination, sodass sie einfacher zu bauen und leichter zu skalieren und zu betreiben sind. Versuchen Sie herauszufinden, ob Sie damit durchkommen, denn normalerweise lohnt es sich, Ihr Problem in eine AP-Lösung umzudefinieren.

Denken Sie daran: Wenn Sie es nicht vermeiden können, verhandeln Sie es zumindest auf ein einfacheres Niveau herunter. Die beste Möglichkeit, ein verteiltes System zu haben, 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 Maulwurfspiel spielen, bei dem die Komplexität auftaucht und Sie sie wieder in den Boden hämmern. Dies wird noch wichtiger, sobald Sie Software schreiben, die sich über mehr als ein System erstreckt – 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 mehrere Möglichkeiten, die Verfügbarkeit zu erhöhen – Sie können einen Knotencluster haben und alles koordinieren (den Arbeitsstatus ständig speichern, damit jeder Knoten alles abrufen kann), aber das erfordert viel Koordination. Koordination macht Dinge brüchig, also ist sie vielleicht nicht möglich? Es gibt verschiedene Möglichkeiten, Koordination zu vermeiden und trotzdem eine gute Verfügbarkeit zu haben:

  • Führen Sie die gleiche Arbeit parallel aus, verwenden Sie jedoch die Ausgabe nur eines Systems. Alles wird auf einem sekundären Knoten repliziert. Wenn also der primäre Knoten ausfällt, stellt die Replikation sicher, dass der Backup-Knoten „heiß“ ist und sofort übernehmen kann. Die Koordination besteht dann nur darin, zu entscheiden, welcher Knoten zuerst ausgeführt wird und welcher Knoten das sekundäre Backup ist.
  • Halten Sie einen Ersatz-Standby bereit. Der primäre Knoten führt regelmäßig Arbeiten auf einem gemeinsam genutzten Speicher fort, und wenn dieser nicht mehr funktioniert, liest der sekundäre Knoten diesen 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, also tun Sie dies, wenn Sie mit einer Koordination auf Konfigurationsebene davonkommen können. Oftmals ist dabei die Wiederholung von Arbeiten erforderlich – ein „genau einmal“-Arbeitsprozess wird zu „fast immer genau einmal, es sei denn, eine Maschine stirbt, und dann wiederholen wir die letzte Minute, um sicherzustellen, dass wir nichts verpassen“. 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. Holen Sie sich eine ehrliche Risikobewertung (wie oft pro Jahr sterben Maschinen einfach?), eine ehrliche Auswirkungsbewertung (wie viele Dinge werden zweimal gemacht und wie wird dies den Benutzern Unannehmlichkeiten bereiten) und eine ehrliche Schwierigkeitsbewertung (zusätzliche Arbeit, mehr Komplexität, die zu mehr Sprödigkeit führt, was zu weniger Verfügbarkeit).

Manchmal ist Verfügbarkeit auch bei Rechenzentrumsausfällen erforderlich. Gehen Sie in diesem Fall besonders vorsichtig vor, denn die Dinge werden besonders schnell besonders brüchig und Sie sollten sicherstellen, dass nur ein minimaler Koordinationsaufwand erforderlich ist.

Verteilen nach Leistung

Manchmal kann man nicht die ganze Arbeit in einem einzigen Knoten erledigen. Versuchen Sie zunächst, nicht in diese Lage zu kommen. Öffnen Sie die Haube und sehen Sie, wo Sie Zyklen verschwenden – diese lästigen LMAX-Leute haben gezeigt, dass Sie auf einer einzigen Maschine siebenstellige Transaktionen pro Sekunde durchführen können; vielleicht sollten Sie sich für die größere Instanz an Amazon wenden. Mittlerweile würde ich erwarten, dass anständige Software Multi-Core-fähig ist, sodass Sie tatsächlich eine schnelle Lösung finden können, indem Sie leistungsstärkere Hardware besorgen. Wenn Sie Ihren Code außerdem nicht so organisieren können, dass er mit mehr Kernen schneller läuft, haben Sie wahrscheinlich keine Chance, ihn durch Hinzufügen weiterer Knoten schneller zu machen, oder? Selbst ohne Engineering auf LMAX-Niveau ist es meiner Meinung nach vernünftig, von Ihrer Software zu erwarten, dass sie mindestens Geschäftsvorgänge im niedrigen fünfstelligen Bereich pro Sekunde abwickelt. Wenn Sie skalieren möchten, weil ein Knoten ein paar Hundert davon pro Sekunde nicht bewältigen kann, sollten Sie vielleicht zunächst noch einmal ganz von vorne anfangen. Höchstwahrscheinlich haben Sie einige 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.

  • Verwenden Sie Konfigurationskoordination statt Transaktionskoordination. Lassen Sie Ihre Knoten ein Koordinationsschema verwenden, um die Arbeit aufzuteilen, und lassen Sie sie dann den Prozess in ihren eigenen Teilen 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 mehr verfügbar ist.
  • Versuchen Sie, die peinlich parallelen Arbeitsschritte zu finden, damit Sie überhaupt keine Koordination benötigen. Als gutes Beispiel fallen mir hier zustandslose Webserver ein, aber das ist nicht der einzige Ort, an dem Sie ein Problem einfach mit einer unkoordinierten Gruppe von Knoten angehen können.

Speicherplatz ist günstig, nutzen Sie ihn

Architekturmuster wie Trennung von Befehl und Abfrage Und Ereignisbeschaffung Entkoppeln und duplizieren Sie die Datenspeicherung häufig in mehrere spezialisierte Phasen. Diese spezialisierten Phasen eignen sich gut zur Unterstützung verteilter Designs, da Sie auswählen können, was lokal gespeichert und was verteilt werden soll, sodass Sie eine Hybridlösung erhalten, die die Koordination minimiert. Sie können beispielsweise Aktualisierungsbefehle in eine verteilte Kafka Cluster, aber alles, was von dort aus 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 viel einfacher zu warten als die klassische Konfiguration, bei der ein zentrales Datenbanksystem der Knotenpunkt aller Vorgänge ist und unweigerlich zum Engpass wird – unabhängig davon, ob das Datenbanksystem für Skalierbarkeit gebaut wurde oder nicht.

Sie können Daten redundant speichern und mehrere unabhängige Systeme ihre jeweils eigene optimierte Form der Daten verwenden lassen. Dies erfordert weniger Koordination 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 besonders, dass Sie es von Grund auf neu erstellen müssen. Es ist sehr wahrscheinlich, dass Sie dafür bezahlt werden, Geschäftsprobleme zu lösen, und nicht dafür, Tools und Infrastruktur zu erstellen. Es gibt also keinen Grund, im Jahr 2017 Dinge selbst herauszufinden. Die korrekte Implementierung eines verteilten Systems ist schwierig, daher werden Sie es wahrscheinlich falsch machen (derselbe Rat gilt übrigens auch für Persistenz und Kryptografie). Wenn Sie glauben, dass Sie ein einzigartiges Problem haben und Ihr eigenes Problem lösen müssen, haben Sie entweder nicht gründlich genug gesucht oder Sie haben sich nicht genug bemüht, Ihr Problem in ein Format zu bringen, das die Verwendung eines der Hunderten von Open-Source-Projekten ermöglicht. Sie haben „das Unternehmen“ dazu gedrängt, die Anforderungen in eine Form zu bringen, die eine verteilte Lösung viel einfacher (und damit zuverlässiger) macht. Jetzt zwingen Sie sich selbst dazu, die richtige Software zu finden, die die nicht einzigartigen Teile Ihres Problems löst, damit Sie sich auf das konzentrieren können, was Ihr Unternehmen besonders macht.

Ja, Werkzeugschmieden macht Spaß – ich liebe es und könnte es den ganzen Tag lang tun. Und tatsächlich ist es gut für Ihr Selbstwertgefühl, Ihr Problem in einer Form zu formulieren, die Sie wie eine einzigartige Schneeflocke aussehen lässt. Vergessen Sie es und lösen Sie echte Probleme, die Art, die Ihr Unternehmen erfolgreich macht.