Zum Inhalt springen
megapromotingLassen Sie uns sprechen

Expertise · Komplexe Architekturen

Mehrere Systeme, die zusammen funktionieren müssen, auch wenn eines davon ausfällt.

Wir entwerfen und betreiben Plattformen aus mehreren Diensten, mit Message-Bus, begrenzten Wiederholungen, Idempotenz-Schlüsseln, Circuit Breakern und Veröffentlichung mit automatischer Rückkehr. Jedes der folgenden Muster läuft in einem eigenen System, nicht in einem Diagramm.

Bereits gebautOperăm patru platforme proprii cu arhitecturi diferite și le putem deschide pe toate. Cifrele sunt măsurate azi cu comenzi, nu preluate din documentație: aichat are 28 de directoare de serviciu, 235 de modele de date pe patru scheme și o magistrală de mesaje cu 297 de cozi și 17.525 de legături; Kallina are 472 de funcții edge, 559 de migrări și 28 de sarcini programate în bază; MEGA CRM are un backend de 9.516 linii cu 99 de rute și 179 de politici de acces pe rând; Taskin rulează 21 de sarcini programate fără niciun Docker. Rezerva pe care o spunem noi: în două locuri documentația proprie a rămas în urma codului — o afirmație despre numărul de linii era depășită cu 16%, iar o diagramă de infrastructură descria un server pe care nu mai rulăm nimic. Am folosit măsurătoarea, nu documentul.

„Komplexe Architektur“ bedeutet nicht viele Kästen auf einer Zeichnung. Es bedeutet, dass Sie wissen, was passiert, wenn einer davon nicht antwortet. Ein System mit drei Diensten, die synchron aufgerufen werden, ohne Wiederholungsgrenze und ohne Idempotenz, ist fragiler als ein Monolith — denn jede neue Verbindung ist eine neue Fehlerart, und Fehlerarten vermehren sich schneller als Funktionen.

Unsere Arbeit beginnt mit der Trennung von Verantwortlichkeiten und endet bei dem, was sichtbar wird, wenn etwas ausfällt. In einer der eigenen Plattformen laufen Nachrichten nicht direkt zwischen Diensten, sondern über einen Nachrichtenbus mit 297 Warteschlangen, 9 Exchanges und 17.525 Verbindungen, davon 278 pro Kunde generierte Warteschlangen, nicht von Hand geschrieben. Acht Warteschlangen haben einen Dead-Letter-Exchange und acht eine Message-TTL von fünf Minuten — das heißt, eine Nachricht, die nicht verarbeitet werden kann, landet irgendwo, wo sie gesehen werden kann, und verschwindet nicht.

Die Muster, die wir einsetzen, sind wenige und wiederholen sich: Wiederholungen mit im Code festgelegter Obergrenze, Idempotenzschlüssel, die eine zweite Zustellung desselben Ereignisses harmlos machen, ein Unterbrecher, der Aufrufe zu einem wiederholt ausfallenden Dienst stoppt, eine Verbrauchsgrenze vor kostenpflichtigen Aktionen und Alarme mit Wiederholschwelle, damit ein intermittierender Fehler nicht den Rest begräbt.

Der letzte Teil, der oft übersehen wird: die Veröffentlichung. Eine der Plattformen wird per Dateisynchronisierung veröffentlicht, ohne Docker, mit der Prüfung, dass die bereitgestellte Seite genau den Kennzeichner des gerade veröffentlichten Pakets enthält — nicht nur, dass der Server 200 zurückgibt — und mit automatischer Rückkehr zur vorherigen Version, wenn die Prüfung fehlschlägt. Die Regel entstand nach einem echten Vorfall, bei dem eine oberflächliche Prüfung eine gute Veröffentlichung aufgehoben hat.

Was dazugehört

Die Arbeit, nach Bestandteilen

Wir trennen Dienste nach dem, was sich unabhängig beschädigen lässt

Auf einer eigenen Plattform gibt es 28 Dienstverzeichnisse, davon haben 16 einen eigenen Einstiegspunkt, und in der Produktion laufen fünf Prozesse unter einem Prozessmanager. Jeder Kommunikationskanal hat seinen eigenen Dienst und seinen eigenen Port, gerade damit ein Fehler bei einem nicht den Rest stoppt. Die Daten sind auf vier separate Schemata modelliert, mit insgesamt 235 Modellen — die Trennung ist nicht nur auf Prozessebene, sondern auch auf Schemaebene.

Wir setzen einen Nachrichtenbus ein, nicht synchrone Kettenaufrufe

297 Warteschlangen, 9 Exchanges, 17.525 Verbindungen. Die Benennung der Warteschlangen ist pro Kunde und in einigen Fällen pro Gesprächsstrang — das heißt, die Topologie wird generiert, nicht von Hand geschrieben. Es gibt auch einen Exchange mit verzögerter Zustellung für Dinge, die später passieren müssen, nicht jetzt. Acht Warteschlangen haben einen Dead-Letter-Exchange konfiguriert und acht eine Message-TTL von fünf Minuten.

Wiederholungen mit Obergrenze, nicht bis ins Unendliche

Drei verschiedene Obergrenzen für drei verschiedene Situationen, alle im Code festgelegt: fünf Wiederholungen für geplante Nachrichten, mit persistentem Zähler, nicht im Speicher gehalten; drei Wiederholungen auf Warteschlangenebene, gezählt in einem Nachrichtenheader; und drei Wiederholungen mit exponentiell steigender Pause für den E-Mail-Versand, auf zwei Anbieterwegen. Eine Wiederholung ohne Obergrenze ist keine Resilienz, sondern eine Schleife.

Idempotenzschlüssel, damit eine zweite Zustellung nichts beschädigt

Für Ereignisse von Dritten verwenden wir einen Insert, der fehlschlägt, wenn das Ereignis bereits gesehen wurde: ein Verstoß gegen die Eindeutigkeit der Datenbank wird als „Duplikat“ behandelt, nicht als Fehler, zusammen mit einer Prüfung der Zeitfrische. Für Zahlungen ist der Schlüssel eine Markierung auf der Kredittransaktion, sodass das erneute Senden desselben Webhooks nicht zweimal gutgeschrieben wird. Für Nachrichten erfolgt die Deduplizierung über die Kennung, mit Lebensdauer und Bereinigung.

Ein Circuit Breaker für die fragile Integration

Die Integration, die von der Sitzung einer externen Website abhängt, hat einen echten Unterbrecher, nicht nur einen Hinweis in der Dokumentation: nach zehn aufeinanderfolgenden Fehlern werden die Aufrufe zwei Minuten lang gestoppt. Ohne ihn verwandelt eine Website, die nicht antwortet, einen ausgefallenen Kanal in eine langsame Plattform, weil alle in der Anfrage warten.

Verbrauchsgrenze vor den kostenpflichtigen Aktionen

In einer der Plattformen läuft jede kostenpflichtige Aktion durch eine Prüffunktion mit vier Stufen, jede mit eigenem klar zurückgegebenem Grund: Konto gesperrt, monatliche Gesprächsgrenze erreicht, tägliches Ausgabenlimit überschritten, unzureichendes Guthaben. Im Ausnahmefall wird „nicht erlaubt“ zurückgegeben, nicht „erlaubt“ — das Tor schließt sich, öffnet sich nicht, wenn etwas nicht funktioniert. Im Hintergrund steht ein Register von Verbrauchsereignissen mit Kosten pro Ereignis, aufgeschlüsselt nach Anbieter, Modell und Modus.

Ratenbegrenzung, die einen Neustart überlebt

Gleitfenster, in der Datenbank gehalten, mit einer Karte im Speicher nur als Abkürzung innerhalb eines Aufrufs. Der Grund steht gleich im Kopf der Datei: Die Prozesse, die die Anfragen bedienen, sind kurzlebig, daher kann ihr Speicher nicht die Quelle der Wahrheit sein. Fünfzehn Funktionen nutzen sie, und das Kontingent eines externen Anbieters hat seinen eigenen, separaten Begrenzer.

Beobachtbarkeit, die zwischen „geantwortet“ und „funktioniert“ unterscheidet

Eine 200-Antwort bedeutet nicht, dass die Aufgabe erfolgreich war. Die Hülle, mit der wir geplante Aufgaben ausführen, durchläuft rekursiv die Antwort nach Fehler- oder Fehlerlisten und behandelt „200 mit Fehlern“ als eigenständiges Ergebnis. Die Alarme haben eine Wiederholschwelle — zehn Minuten in einem System, zwei Stunden im anderen — und die Markierung wird beim ersten Erfolg gelöscht, damit ein wiederkehrender Defekt sofort erneut alarmieren kann.

Veröffentlichung mit Prüfung und automatischer Rückkehr

Die Veröffentlichung prüft nicht nur, dass die Adresse 200 zurückgibt, sondern dass die ausgelieferte Seite genau den Kennzeichner des gerade veröffentlichten Pakets enthält; andernfalls wird die vorherige Version wiederhergestellt. Für den Hintergrunddienst vergleicht die Veröffentlichung den Baum per Prüfsummen und überspringt den Neustart, wenn sich nichts geändert hat — ein unnötiger Neustart tötet eine laufende Schleife.

Wie es aussieht

Der Weg, Schritt für Schritt.

01

Wir zeichnen die Karte der Fehlermodi, nicht der Boxen

Für jede Verbindung zwischen zwei Systemen: Was passiert, wenn das andere langsam ist, wenn es ausgefallen ist, wenn es zweimal antwortet, wenn es falsch antwortet. Wir liefern: die Liste der Verbindungen mit dem erwarteten Verhalten in jeder der vier Situationen sowie die synchrone/asynchrone Entscheidung für jede.

02

Wir setzen den Bus und die Nachrichtenverträge auf

Warteschlangen, Exchanges, Dead-Letter, Lebensdauer pro Nachricht und der Idempotenzschlüssel für jeden Ereignistyp. Wir liefern: die Topologie, die Nachrichtenverträge und das dokumentierte Verhalten bei Wiederholung.

03

Wir fügen die Tore hinzu: Rate, Verbrauch, Schalter

Die Ratenbegrenzung mit gleitendem Fenster in der Basis, das Verbrauchstor vor den kostenpflichtigen Aktionen und der Schalter bei Integrationen außerhalb unserer Kontrolle. Wir liefern: die gesetzten Schwellenwerte, die klar zurückgegebenen Gründe und die Tests für jede Ablehnungsstufe.

04

Wir bauen Veröffentlichung und Rückkehr

Veröffentlichung mit Inhaltsprüfung, nicht nur mit Antwortcode, vorherige Version daneben gespeichert und automatische Rückkehr. Wir liefern: das Veröffentlichungsverfahren, das mindestens einmal geübte Rückkehrverfahren und die Gesundheitsprüfungen — eine oberflächliche für „lebt“, eine tiefe für „funktioniert wirklich“.

05

Wir übergeben mit gegenüber dem Code geprüfter Dokumentation

Das Dokument wird vor der Übergabe mit der Messung abgeglichen, denn eine Dokumentation, die hinterherhinkt, ist gefährlicher als ihr Fehlen. Wir liefern: das Diagramm, die Verfahren und die explizite Liste der Stellen, an denen Dokument und Code im Widerspruch gefunden wurden, samt dem, was wir korrigiert haben.

DocumenteConversațiiSurseSintezăAcțiuneEchipăContexteazăSurse autorizate. Acțiuni revizuite.
Sistemele nu se apelează în lanț: între ele stă o magistrală cu cozi per client, schimburi separate pe tip de trafic și scrisori moarte pentru ce nu se poate procesa. În jurul ei, porțile — limitare de rată, poartă de consum, întrerupător de circuit — și sondele de sănătate, una superficială și una adâncă.

Die Daten

Was wir anfassen, wo die Daten liegen und wie lange sie bleiben

Die Fragen, die jede Person mit einem Datenschutzbeauftragten stellt — hier gestellt, bevor er sie stellt.

Wo die Daten liegen, pro Plattform
Es gibt keine einheitliche Antwort, und das ist gut so: Eine Plattform verwendet MySQL auf einem eigenen Server, zwei verwenden PostgreSQL über Supabase — eine gehostet, eine auf unserer Infrastruktur installiert — und eine hält den Inhalt als statische Dateien, ganz ohne Datenbank. Die Wahl erfolgt nach Anforderungen, nicht nach Gewohnheit.
Der Zugriff gilt in der Datenbank, nicht nur in der Anwendung
In einem internen System von uns ist Row-Level-Sicherheit auf 43 Tabellen aktiviert, über 90 Anweisungen, mit 179 geschriebenen Policies. Die Regel, der wir folgen: Wenn ein Aufruf die Anwendung umgeht und direkt die Datenbank trifft, darf er trotzdem nicht die Zeilen eines anderen sehen.
Das Verbrauchsregister
Eine Zeile pro Ereignis mit Typ, Anbieter, getrennten Mengen je Modalität — Gesprächssekunden, Eingangstokens, Ausgangstokens, Zeichen — plus Kosten in Währung mit sechs Dezimalstellen und abgezogenen Credits. Daraus ergeben sich das tägliche Ausgabenlimit, die Kosten pro Aufruf und die Verbrauchsprognose.
Migrationen sind die Historie, nicht die Dokumentation
559 Migrationen in einer Plattform, 80 in einer anderen. Das Schema wird über versionierte Migrationen geändert, sodass sich der Datenbankzustand rekonstruieren und chronologisch lesen lässt. Wenn das Dokument und die Migration nicht übereinstimmen, hat die Migration recht.
Geplante Aufgaben liegen in der Datenbank oder in Cron, nicht im Prozess
28 geplante Aufgaben laufen in einer Plattform innerhalb der Datenbank; in einer anderen laufen 21 Aufgaben über System-Cron. Der Grund steht in der Datei: Timer innerhalb eines Dienstes werden bei jedem Neustart zurückgesetzt, daher wird eine Drei-Stunden-Schleife in einem Dienst, der neu bereitgestellt wird, nie ausgelöst.

Ein Fall

Eine Nachrichten-Topologie, die sich für jeden Kunden selbst erzeugt

Die Ausgangslage

Eine Plattform mit mehreren Kunden, jeder mit eigenen Kommunikationskanälen, eigenen Gesprächsverläufen und eigenen Benachrichtigungen. Die naive Variante — eine gemeinsame Warteschlange und ein Filter nach Kundenkennung — führt dazu, dass ein Kunde mit hohem Volumen den Rest blockiert, und ein Fehler in einem Verlauf die Warteschlange für alle anhält.

Was wir gebaut haben

Die Topologie wird pro Kunde erzeugt, nicht von Hand geschrieben: Von 297 Warteschlangen sind 278 vom Typ „Benachrichtigungen für ein bestimmtes Konto“, und einige gehen bis auf die Ebene eines einzelnen Gesprächsverlaufs herunter. Darüber hinaus trennen neun Exchanges die Verkehrstypen — Benachrichtigungen, Token, Webhooks, Kanäle — und es gibt einen Exchange mit verzögerter Zustellung für das, was später geschehen soll. Acht Warteschlangen haben einen Dead-Letter-Exchange, acht haben eine Message-TTL von fünf Minuten. Die Orchestrierungsregeln können live über einen Publishing-Kanal neu geladen werden, ohne die Consumer neu zu starten.

Was dabei herauskam

Ein Kunde mit hohem Volumen verzögert die anderen Kunden nicht, und eine Nachricht, die nicht verarbeitet werden kann, landet an einem Ort, an dem sie gesehen und erneut versucht werden kann, statt zu verschwinden oder die Warteschlange zu blockieren. Die Anzahl der Verbindungen im Bus — 17.525 — zeigt genau, warum die Topologie generiert werden muss: Niemand pflegt so etwas manuell.

Was der Fall nicht sagt

Der Preis ist betrieblich: Ein Bus dieser Größe braucht eigene Überwachung und einen Plan für verlassene Warteschlangen, sonst wächst er endlos weiter. Und die Generierung pro Kunde setzt voraus, dass das Löschen eines Kunden auch seine Topologie löscht — wenn dieser Schritt fehlt, sammeln sich tote Warteschlangen an.

Fragen

Was uns die Leute fragen, bevor sie anrufen

Woher weiß ich, dass Sie mir keine Komplexität verkaufen, die ich nicht brauche?

Weil die erste Empfehlung, die wir oft geben, ist, nicht zu trennen. Jeder neue Dienst ist ein neuer Ausfallmodus, und Ausfallmodi vermehren sich schneller als Funktionen. Ein Beispiel aus unseren eigenen Systemen: Das Backend eines davon ist eine einzige Datei mit 9.516 Zeilen und 99 Routen. Es ist nicht elegant, und wir sagen das auch so, aber es wird in einem Schritt bereitgestellt und an einem Ort debuggt. Die Trennung erfolgt, wenn es einen messbaren Grund gibt — einen Dienst, der separat skaliert werden muss, ein separates Team, ein separater Veröffentlichungsrhythmus.

Was passiert, wenn ein System in der Kette nicht antwortet?

Es hängt davon ab, was wir in der Kartenphase gemeinsam entschieden haben, und genau das ist der Punkt. In unseren Systemen: Die Nachricht geht in die Warteschlange und wird eine begrenzte Anzahl von Malen erneut versucht, dann gelangt sie in den Dead-Letter-Exchange, wo sie sichtbar sein kann; die instabile Integration hat einen Schalter, der nach zehn aufeinanderfolgenden Fehlern zwei Minuten lang stoppt, statt Anfragen in der Warteschlange zu halten; und Aktionen, die Geld kosten, werden von einem Gate gestoppt, das im Ausnahmefall ablehnt, nicht zulässt.

Wie verhindern Sie, dass dasselbe Ereignis zweimal verarbeitet wird?

Mit einem Idempotenzschlüssel, nicht mit einer „Habe ich das schon gesehen?“-Prüfung, die eine Datenrace hat. Konkret: Wir fügen eine Zeile mit der Ereignis-ID ein und wenn die Datenbank wegen Verletzung der Eindeutigkeit ablehnt, behandeln wir genau diesen Fehlercode als Duplikatssignal, nicht als Defekt. Bei Zahlungen liegt die Markierung auf der Gutschriftstransaktion, also führt das erneute Senden desselben Webhooks nicht zu einer doppelten Gutschrift.

Verwenden Sie Docker oder nicht?

Beides, und wir begründen die Wahl jedes Mal. Ein System läuft im Container, aber ohne eingehängte Volumes, was bedeutet, dass die Bereitstellung ein Kopieren in den Container plus Neustart ist — ein `docker rm` dort verliert den Zustand, und genau so steht es im Betriebsdokument. Eine andere Plattform hat überhaupt kein Docker: Die Bereitstellung ist Dateisynchronisierung, und die Bereitstellungsskripte werden manuell mit Administratorrechten installiert und dem automatischen Ablauf über genau zwei erlaubte Wege zugänglich gemacht. Der Grund steht im Code: Ein Skript, das der Ablauf überschreiben kann, ist ein Skript, über das der Ablauf eskalieren kann.

Wie wissen Sie, dass ein geplanter Task tatsächlich gelaufen ist?

Aus schlechter Erfahrung. Ein unser täglicher Bericht war elf Tage lang tot, zwischen dem 7. und 17. August, während der geplante Task jeden Tag ausgelöst wurde — der verwendete Befehl beendete sich bei Fehlern still und schrieb nichts. Seitdem läuft jeder Task in einem Wrapper, der die Antwort nach Fehlerlisten durchsucht, „200 mit Fehlern“ als separates Ergebnis behandelt, eine maximale Laufzeit hat und pro Lauf eine strukturierte Zeile schreibt. Die Alarme haben eine Wiederholschwelle, und die Markierung wird beim ersten Erfolg gelöscht.

Wie wichtig ist die Abstraktion vom Anbieter?

Sehr, wenn der Anbieter in einem Bereich ist, der sich schnell bewegt. Für Sprache haben wir drei separate Brücken — je eine für jeden Echtzeit-Sprachanbieter —, die dieselbe Schnittstelle implementieren. Die Brücke übernimmt die Audiokonvertierung zwischen der Telefonanlage und dem Anbieter, auf einem dedizierten Port, mit dem Health-Server nur an die lokale Schnittstelle gebunden, nicht exponiert. Der Anbieterwechsel wird zu einer Entscheidung, nicht zu einer Neuschreibung.

Ist Ihre Dokumentation auf dem neuesten Stand?

Nicht überall, und wir sagen lieber, wo. Bei der Vorbereitung dieser Seite haben wir zwei Systeme gemessen und unsere eigene Dokumentation hinter der Realität gefunden: Eine Aussage über die Größe einer Datei lag um etwa 16% unter dem tatsächlichen Wert, und ein Infrastrukturdiagramm beschrieb einen Server, von dem wir bereits umgezogen waren. Die Regel, die wir anwenden und in Projekten verlangen: Wenn Dokument und Messung nicht übereinstimmen, hat die Messung recht, und das Dokument wird im selben Schritt korrigiert.

Was machen Sie nicht?

Wir versprechen keine Verfügbarkeitsziele ohne Messung — ein „99,9%“-Wert erfordert Felddaten über einen Zeitraum, und wo wir sie nicht haben, behaupten wir ihn nicht. Wir entwerfen keine Systeme, die wir nicht betreiben oder übergeben können: Wenn das Ergebnis eine Architektur ist, die Ihr Team nicht warten kann, ist es die falsche Architektur. Und wir machen keine Compliance-Zertifizierungen — wir können die Kontrollen bauen, wir können das Zertifikat nicht ausstellen.

Worauf die obigen Aussagen beruhen (23 Quellen)

23 davon sind Code und Dateien aus unseren Repositories. Wir veröffentlichen weder ihren Namen noch die Zeile: zusammen, auf einer einzigen Seite, würden sie zu genau beschreiben, wie Systeme gebaut sind, die nicht nur uns gehören. Wir gehen sie mit Ihnen durch, im Repository, auf Anfrage — die Überprüfung bleibt möglich, sie findet nur in einem Gespräch statt.

Was sollte bei Ihnen besser laufen?

Erzählen Sie uns von Ihrem Prozess. Gemeinsam legen wir fest, was sich zu bauen lohnt, was wir anbinden können und wie wir das Ergebnis prüfen.

Lassen Sie uns sprechen