banner

Blog

Apr 21, 2024

Die Punkte verbinden: API-Design in einer verteilten Welt

InfoQ-Homepage-Präsentationen Connecting the Dots: API-Design in einer verteilten Welt

Ben Gamble untersucht API-Design aus der Sicht eines Entwicklers und Verbrauchers interner und externer APIs.

Ben Gamble war über 10 Jahre lang als technischer Leiter für Startups und wachstumsstarke Unternehmen tätig. Als Gründer, CTO, Produzent und Produktführer hat er die Lücke zwischen Forschung und Produktentwicklung geschlossen. Da er mit modernster Augmented Reality, der Skalierung von 3D-Spielen und der Logistik am selben Tag gearbeitet hat, ist es ihm nicht fremd, sich technischen Herausforderungen zu stellen.

Software verändert die Welt. QCon stärkt die Softwareentwicklung, indem es die Verbreitung von Wissen und Innovation in der Entwicklergemeinschaft erleichtert. QCon ist eine von Praktikern geleitete Konferenz und richtet sich an technische Teamleiter, Architekten, technische Leiter und Projektmanager, die Innovationen in ihren Teams beeinflussen.

Präsentiert von: Tomasz Grabiec – Distinguished Engineer bei ScyllaDB und Tzach Livyatan – VP of Product bei ScyllaDB

Treffen Sie die richtigen Entscheidungen, indem Sie herausfinden, wie leitende Softwareentwickler bei Early-Adopter-Unternehmen neue Trends übernehmen. Jetzt registrieren!

Gamble: Mein Name ist Ben Gamble. Ich arbeite derzeit an einem Ort namens Aiven. Ich arbeite in den Developer Relations-Teams als eine Art Open-Source-Software-Sommelier. Ich bin hier, um Ihnen etwas Kafka, etwas Postgres und vielleicht etwas Käse dazu zu servieren. Meine Geschichte ist eine ziemlich bewegte Vergangenheit, die von der Entwicklung von MMO-Spielen von den zentralen Netzwerkebenen aufwärts bis hin zur Entwicklung medizinischer Geräte für die eigentliche Point-of-Care-Diagnose reicht. Ich habe mich ein wenig in der Gegend umgesehen. Ich habe in den letzten 12 Monaten tatsächlich an der Produktion von Pascal gearbeitet, bei der es darum ging, MQTT-Streaming an einem früheren Ort durchzuführen, an dem ich gearbeitet habe. Ich lerne gerade Rust, damit ich Krustentierwitze besser erzählen kann. Denn es gibt so etwas auf der Welt, und zwar schon seit, glaube ich, ein paar Millionen Jahren, und es heißt Karzinisierung. Hier versucht die Natur immer wieder, Dinge zu Krebsen zu entwickeln. Das ist tatsächlich eine wirklich dokumentierte Sache, und Sie können es am Computer herausfinden, wenn Sie es nachschlagen. Aus diesem Grund liebt die Natur Krabben und möchte, dass alles eins ist. Deshalb arbeite ich bei Aiven, weil es ein wunderschönes Krabbenlogo hat. Irgendwann könnten auch Sie alle uns gebrauchen. Aiven ist eine Open-Source-Hosting-Plattform zum Hosten Ihrer Datenanforderungen. Wir machen Dinge wie Postgres im großen Maßstab. Wir machen Dinge wie Kafka in großem Maßstab. Wir sind dafür da, den Zugriff auf Open-Source-Software zu erleichtern.

Das große Thema hier ist, dass diejenigen, die sich nicht an die Vergangenheit erinnern können, dazu verdammt sind, sie zu wiederholen. Dies ist ein altes Zitat von George Santayana über die Bedeutung der Vernunft aus dem Jahr 1905. Das Gemälde hier stammt aus dem Smithsonian. Es geht wirklich darum, dass die großartigen Ideen des Menschen immer wieder das Gleiche sind. Dies ist das Thema, das man in allem sehen kann, von der Kunst bis zum Leben, aber auch in der Softwareentwicklung, denn in den meisten Fällen machen wir das Gleiche noch einmal und im Idealfall besser. Lassen Sie uns vor diesem Hintergrund über APIs nachdenken und uns an Monolithen erinnern. Erinnern Sie sich an diese Dinge, sie waren wunderschön. Es waren große Systeme, die man aufstellen konnte und die einfach liefen. Es handelte sich um ein einzelnes, aus Stein gemeißeltes Gebilde, ein bisschen wie das alte aus dem Film. Das Entscheidende dabei ist, dass fast immer etwas zwischen Ihnen, Ihrem System und Ihren Kunden war, oft ein Load Balancer. Darin waren eine Reihe von Werkzeugen eingebaut. Dann hatte man fast immer eine relationale Datenbank darunter. Je nachdem, aus welcher Ära Sie stammen, kann dies alles sein, von MS SQL über Oracle bis hin zu Db2 und in jüngerer Zeit auch MySQL und Postgres und sogar einige modernere Varianten davon. Das Coole daran ist, dass wir zwischendurch wirklich nur eine große Sache hatten. Beim Skalieren haben Sie einfach mehr RAM-Sticks oder schnellere CPUs hinzugefügt, insbesondere damals, als Sie Ihre CPU buchstäblich jedes Jahr aktualisieren konnten, und wahrscheinlich ist alles in Ordnung.

Wenn wir über die APIs dieser Systeme sprechen, denken wir dieses Mal an SOAP. Im Gegensatz zum Film hatten wir ein Gespräch über SOAP. Lassen Sie uns darüber sprechen, was SOAP wirklich ist. SOAP war ein Protokoll. Es definierte, wie Sie über Dinge sprechen. Es definierte die Verschlüsselung. Es definierte eine Möglichkeit, Ihre Daten über die Leitung anzuordnen, in diesem Fall XML. Da darin alle verschiedenen Schritte auf dem Weg beschrieben wurden, war es eine ziemlich vollständige Sache. SOAP war ein wunderbares Sprungbrett in der Entwicklung von APIs, insbesondere für die Kommunikation über das Web. Dadurch konnten wir strukturierte Anwendungen erstellen, die verstanden, woher sie kommen. Es entstand aus den ursprünglichen CORBA- und RPC-Frameworks, war aber eigentlich auf die gemeinsame Nutzung von Daten ausgerichtet. Es war komplex und schwer. Die Implementierung von SOAP-Schnittstellen nahm Zeit in Anspruch. Es waren viele Bits erforderlich. Es war nicht wirklich agil genug, um dem wachsenden Boom moderner Software gerecht zu werden. Vor diesem Hintergrund war es Zeit für RUHE. Wir reden hier von Representational State Transfer. Bekanntermaßen eine Weiterentwicklung als Alternative zu SOAP, die als großer Fortschritt angesehen wurde und bis vor relativ kurzer Zeit de facto die Möglichkeit war, Dinge online zu erledigen. Das Beste an REST ist, dass es einfach zu erstellen und zu starten ist. Dies ist der Boom von fast Web 1.0 und 2.0, die Möglichkeit, REST-APIs sehr schnell mit Stacks wie dem ursprünglichen LAMP-Stack, Linux, Apache, MySQL und oft Perl, PHP oder Python bereitzustellen. Klassischerweise hat PHP für den LAMP-Stack das moderne Web hervorgebracht. Mit seiner damals aufrichtigen Schmeichelei und Nachahmung kamen wir auf Systeme wie Ruby on Rails, Django in Python und Node.js mit Express. Das waren die Dinge, die das Internet, wie wir es kennen, wirklich explodieren ließen. Wie alles dieser Art waren auch diese einfach herzustellen, einfach zu starten und unglaublich skalierbar. Ein Teil davon ist einfach auf die Single-Threaded-Natur der meisten Software zurückzuführen, in die sie integriert sind. Das ist nicht die ganze Geschichte. Das eigentliche Problem entsteht, wenn man tatsächlich anfängt, dies praktisch umzusetzen.

Sie beginnen mit der Skalierung mit Best Practices. Sie haben Ihre REST-API und ein Frontend kommuniziert mit ihr. Sie denken, ich habe hier einen Konflikt um meine Datenbank, ich werde einfach mehr Versionen dieser API erstellen und eine hochverfügbare Datenbank haben. Ich habe viele APIs, die alle genau gleich sind. Wenn ich meine Datenbanken skaliere, kann ich die Leselast nur woanders hin verschieben. Das nächste ist, dass ich immer noch langsam arbeite, denn fast immer ist die Datenbank das erste, was sie unter der Last belastet. Also los, fügen wir etwas Caching hinzu, das macht alles besser. Redis ist wie die dunkle Magie der Webwelt, es fügt einfach hinzu und lässt Dinge verschwinden, wirklich, es verschiebt Probleme. Das nächste, was Sie tun, ist, dass Ihre API mit zunehmenden Funktionen komplizierter wird, da verschiedene Arten von Benutzern Ihre API verwenden. Das mobile Erlebnis unterscheidet sich stark vom Web-Erlebnis, weil es unterschiedliche Erwartungen und unterschiedliche Netzwerklasten gibt. Außerdem gibt es dieses konkrete Formfaktorproblem, das Sie verwenden, um unterschiedliche Informationen bereitzustellen. Wenn dies geschieht, spezialisieren Sie sich letztlich auf die Art und Weise, wie Sie Daten bereitstellen, wo auch immer sie sich befinden, indem Sie einen GraphQL-Endpunkt offenlegen oder indem Sie tatsächlich spezielle Datendienste hinter den Kulissen haben. Sie sprechen jetzt mehr unterschiedliche APIs und müssen mehr Dinge offenlegen. Sie denken: „Das wird immer komplizierter. Lassen Sie uns etwas dazwischen legen, um zu helfen.“ Sie trennen die APIs. Vielleicht haben Sie ein System, das eine Reihe von Dingen erledigt, und ein anderes System, das andere erledigt. Vielleicht bedient einer Ihr Inventar und Ihren Katalog und der andere kümmert sich tatsächlich um die Anmeldung, und Sie schieben ein API-Gateway in den Vordergrund. Dies ist die frühe Phase, in der Sie herausfinden, was Sie tun möchten.

Man macht eigentlich nichts anderes, als zu sagen: „Dieses System dient einem Zweck, dieses System dient einem anderen Zweck, so etwas wie die serviceorientierte Architektur aus der Mitte der 2010er Jahre.“ Sie schieben alles hinter ein API-Gateway, das als Ihr Load Balancer fungiert. Damit kommt man wirklich weit. Sie können von diesem „Nichts teilen“-Ansatz, bei dem sich alles in einer Maschine befinden muss, zu einem „Teilen von fast allem in Echtzeit“ übergehen. Sie können diese API-Ebenen tatsächlich trennen, sodass die Person, die die obere API erstellt, nicht unbedingt etwas über die Person wissen muss, die die untere API erstellt. Wenn Sie anfangen, dies zu tun, gibt es noch mehr Dinge zu tun, weil Sie plötzlich erkennen, dass Sie Dinge wie nicht nur Suchen, sondern auch umsetzbare Erkenntnisse tun können, weil Sie über eine API-Ebene verfügen, die alles kann, weil Sie einfach weitere Bits hinzufügen können Es. Mit Systemen im Hintergrund können Sie diese Daten den Personen zur Verfügung stellen, die sie benötigen. Es geht darum, die Benutzererfahrung präventiv zu verbessern. Denn was jetzt passiert, ist, dass wir vor allem, was wir tun, eine Fassade haben und dass wir die Möglichkeit haben, einfach Verarbeitungs-Engines hinter die Kulissen zu werfen, um damit zu beginnen, Echtzeit- oder auch nur entsprechende Daten über das Web bereitzustellen.

Hier haben wir die Idee der richtigen Microservices mit den richtigen Daten. Hierher kommt das Microservice-Ding, das ich zuvor angedeutet habe, nämlich die Idee eines Dienstes, der eine Sache tut und dann über die dazugehörigen Daten verfügt. Dies ist der moderne Datenstapel, von dem Sie immer wieder hören. Wenn Sie einige der Vorträge auch in diesem Track gesehen haben, haben Sie sicher schon von der Auswahl des richtigen Datentools gehört, das den Anforderungen Ihrer Microservices entspricht. Dies ist jedoch nicht das Ende der Geschichte, denn was wir hier haben, ist, dass es all diese verschiedenen Dienste gibt, und einige von ihnen kommunizieren über eine wunderbare Datenebene wie Kafka, oder Service Meshes oder fast alles andere. Was hier jedoch nicht wirklich gezeigt wird, ist die Anzahl der internen APIs. Buchstäblich jede Kante dieses Diagramms verfügt über eine API, jedes Kästchen, sogar innerhalb dieser Kästchen, kommuniziert zwischen ihnen. Es gibt zahlreiche Dinge, die sich mit SQL befassen, einige Dinge, die sich mit Redis befassen, viele Dinge, die sich mit Kafka befassen, alles, was mit REST auf der einen oder anderen Ebene zu tun hat. Andererseits reden andere Dinge über CQL. Wenn man zu dieser Sache kommt, stellt sich die Frage: Wie viele APIs habe ich wirklich? Habe ich nur die oben oder die zwischen meinem API-Gateway und meinen Diensten oder nachdem ich alle berücksichtigt habe? Dann müssen Sie bedenken, dass Sie als Nächstes möglicherweise eine andere Datenbank haben. Möglicherweise gibt es eine andere Caching-Richtlinie. Es könnte eine völlig andere API haben. Möglicherweise sind sogar noch mehr Regeln erforderlich. Denn was passiert, wenn Sie einen Compliance-Dienst hinzufügen, der aufgrund gesetzlicher Anforderungen nur innerhalb Aserbaidschans betrieben werden kann? Jetzt muss diese API zusätzliche Regeln haben.

Um dies in einen Zusammenhang zu bringen, erzählen wir eine Geschichte. Das hängt mit etwas zusammen, das ich früher bei meinem früheren Arbeitgeber gemacht habe. Sie haben eine Plattform als Service. In diesem Fall sprechen wir von großen Streaming-Systemen. Wir möchten eine API erstellen, um programmatische Konfigurationen zu ermöglichen. Das liegt daran, dass man früher mit diesem wunderbaren Dashboard, mit dem man die Anwendungen konfigurieren konnte, wirklich weit gekommen ist, und sie haben es einfach in Betrieb genommen, und dann sind die Anwendungen automatisch gewachsen und skaliert. Das Problem besteht darin, dass es mit der Weiterentwicklung der Infrastruktur als Codebewegung, aber auch aufgrund der Tatsache, dass Ihr System wirklich über eine sehr hohe Flexibilität verfügt, immer wichtiger wird, Änderungen an Ihrem tatsächlichen Setup aus der Ferne vornehmen zu können. Das erste, woran Sie denken, ist natürlich, dass ich von der OpenAPI-Initiative gehört habe, einem Gremium von Open-Source-Leuten, die eine Möglichkeit zur Spezifikation von REST-APIs entwickelt haben. Die Sache damit ist, Sie denken, ich kann es einfach spezifizieren, und Sie denken, eine REST-API. REST-APIs sind großartig, weil jeder weiß, was sie tun. Es gibt dort idempotente Verben, also besteht die Idee eines GET darin, dass Sie jedes Mal das Gleiche erhalten, oder eines PUT, wenn Sie es einmal tun, wird es jedes Mal das Gleiche tun. POST ist das Einzige, worüber Sie sich wirklich Sorgen machen müssen, aber es kommt am häufigsten vor. Noch wichtiger ist, dass es weit verbreitet ist. Da es sich um eine offene Spezifikation handelt, gibt es hier keine wirklichen Einschränkungen. Sie denken: Ja, ich habe diese Plattform, ich werde ihr eine API hinzufügen. Ich werde es möglich machen. Jeder darf damit fahren, wie er möchte.

Lassen Sie uns darüber sprechen, was hier schief gehen kann, denn wir haben es versucht, aber es stellte sich als ziemlich kompliziert heraus. Das erste und größte Problem waren letztendlich undichte Abstraktionen. Was hier passiert, ist, dass unsere gesamte Plattform zu einem Microservice für die Plattformen aller anderen geworden ist. Während Sie Ihr Microservice-System aufbauen, integrieren Sie nun externe Systeme als separaten Service mit einer API. Wo wir zu undichten Abstraktionen kommen, ist eigentlich ziemlich ernst, denn was passierte, war das erste Mal, dass wir eine völlig undichte API hatten, die Details von Produktionssystemen offenlegte. Dies ähnelte der logischen Gruppierung der APIs. Sie wurden nicht semantisch nach dem gruppiert, was sie tatsächlich taten, sondern nach dem Zeitpunkt, an dem sie erstellt und dem System im Backend hinzugefügt wurden, denn so wurden sie im Backend gruppiert An welchem ​​Stück interner Technologie hingen sie fest? Das bedeutete, dass man ohne viel Insiderwissen am Ende ein problematisches System hatte, das sehr schwer zu verstehen war. Da jemand gerade eine der internen APIs gespiegelt hat, haben wir außerdem eine sehr unkontrollierte Angriffsfläche, da wir keine wirkliche Möglichkeit hatten, all diese verschiedenen APIs zu kennen, die wir treffen werden. Machte Sinn, denn es war nur ein Spiegel. Da hat niemand gedacht, das ist etwas Richtiges.

Das andere Ende des Systems ist der Weg zur Abstraktion. Ich habe einige davon gesehen, was wieder ein Versuch bei einem anderen Unternehmen war. Was passiert ist, war, dass wir einen magischen Knopf, eine One-Shot-API, erstellt haben, um etwas zu tun. In diesem Fall wurde eine Engine konfiguriert, die eine Adressüberprüfung durchführen konnte. Bei der Einrichtung der Adressverifizierungs-Engine hieß es im Grunde: „Geben Sie mir eine Engine und setzen Sie deren Einstellungen auf 5.“ Das Gute daran ist, dass es sehr einfach durchzuführen ist. Das Schreckliche daran ist, dass Sie keine wirkliche Kontrolle haben, da Ihre API im Hinblick auf den Benutzer und nicht im Hinblick auf das System entwickelt wurde. Ein Benutzer ist es vielleicht gewohnt, ein paar Knöpfe zu drücken, um die richtigen Einstellungen zu erreichen, aber das spiegelt nicht wirklich die Funktionsweise der Interna auf sinnvolle Weise wider. Du bist zu abstrakt. Es bedeutet so etwas wie: Was passiert dann wirklich, wenn ich diese Schaltfläche auf 11 setze? Ist 11 tatsächlich ein Punkt mehr als 10 oder ist es 100 Mal mehr? Am Ende benötigen Sie eine sehr lange erläuternde Dokumentation, um die Leute in die richtige Einstellung zu bringen, damit sie Ihre Software tatsächlich nutzen können.

Hier möchten Sie keine Implementierungsdetails preisgeben, sondern die Realität der Implementierung projizieren. Wenn die Tatsache ist, dass Einstellung 1 nur eine Maschine und Einstellung 3 tatsächlich 30 Maschinen umfasst, möchten Sie diese möglicherweise in etwas umbenennen, das besser zu den Vorgängen im Inneren passt. Wenn Sie jedoch nie eine Einstellung zwischen den beiden haben, möchten Sie auf jeden Fall sicherstellen, dass sie nur so belichtet wird, dass klar erkennbar ist, dass es keine dazwischen liegenden Einstellungen gibt. Dies richtig zu machen, ist kein Ende der Herausforderung. Im Fall, dass wir beim ersten Mal eine sehr undichte Abstraktion hatten, mussten wir sie am Ende häufig umgestalten, um zu versuchen, unsere Absicht widerzuspiegeln. Am Ende hatten wir jedoch Leute, die das in der Produktion hatten, und plötzlich kann man bestimmte undichte Abstraktionen nicht mehr wirklich entfernen. Wir mussten voranschreiten und dafür sorgen, dass es klappt. Anschließend haben wir alle Lücken geschlossen, die Angriffsflächen eingeschränkt und hinter den Kulissen weitere Dinge hinzugefügt, aber die API war nicht verfügbar. Sobald etwas draußen ist, wird es genutzt und man kann sich darauf verlassen.

Das nächste wirkliche Problem war die Verfügbarkeit. Bei einer Plattform als Service bieten Sie oft ein sehr gutes SLA an, etwa eine Anzahl von Neunen. In diesem Fall war fünf der niedrigste Betrag, den wir angeboten haben, was, gelinde gesagt, lustig ist. Allerdings sind Ihre Dashboard-Dienste und die Dienste auf Ihrer Steuerungsebene Ihres Systems nicht immer gleich. Es ist eine ganz andere Sache, etwas Neues bereitzustellen, als es tatsächlich zu betreiben. Die Fähigkeit zu sagen: „Gib mir eine neue Instanz von X, Y, Z in der Region ABC“ unterscheidet sich stark von der Fähigkeit, auf die Region zuzugreifen, sobald sie dort ist. Man muss je nach den verschiedenen Teilen des Systems planen, diese sind oft nicht an der gleichen Stelle oder auf dem gleichen Niveau verfestigt. Was hier wirklich besorgniserregend ist, ist, dass das eigentliche Frontend Ihrer Website in vielen Fällen ein und dasselbe ist. Das trifft jetzt weniger zu und trifft immer weniger zu. Eine Zeit lang war es eine Sache. Ich habe mich mit vielen Systemen befasst, bei denen dies auftrat. Plötzlich haben Sie Ihre API-Oberfläche mit Ihrem Blog gekoppelt, und da Sie nun an einigen Stellen monolithisch geworden sind und an anderen nicht, müssen Sie Ihr Blog jetzt genauso robust haben wie Ihren Kerndienst. Das ist nicht unbedingt ein Problem, aber es legt die Messlatte dafür höher, woher Ihre tatsächlichen Verfügbarkeitsprobleme kommen. Ist es in Ordnung, dass Ihr Five Nines-Verfügbarkeitsdienst, der eigentlich nicht so viele Zugriffe erhält, plötzlich von einem Stack-Overflow-Kommentar überschwemmt wird, der vollkommen gültig viral geht? Die Antwort ist nein. Man muss wirklich sehr gründlich darüber nachdenken.

Die Antwort hier war natürlich: Man muss es entkoppeln. Sie müssen es so entkoppeln, dass Sie nichts kaputt machen, wo immer es bedeutet, dass Sie einfach sicherstellen, dass sich nichts im selben Ressourcenpool befindet, oder dass Sie Ihren Load Balancer irgendwo heraustrennen, oder dass Sie tatsächlich einen haben API-Gateway, das dies für Sie erledigt. Es ist bis zu einem gewissen Grad die Entscheidung des Händlers. Der entscheidende Punkt hier ist die Geschichte. Die Geschichte ist einfach: Wenn Sie tatsächlich ein System haben, von dem die Dinge abhängen, ist es Teil Ihres Produkts geworden. Daher gilt Ihr Produkt-SLA für festgelegte APIs. APIs sind Produkte, unabhängig davon, ob Sie Ihr Produkt als API selbst oder als eine Dienstleistung betrachten. Grundsätzlich interagieren Sie über eine API damit. Daher ist die API selbst ein Produkt. Die Konfiguration ist nun, sofern Sie sie programmatisch anbieten, ebenfalls Teil des Produkts. Das hat uns ziemlich hart getroffen. Es wurde behoben und es war ein tolles Stück Arbeit, es erledigt zu sehen. Eine Rails-API, die ursprünglich nur ein Monolith in einer Region war, plötzlich in den 15 bis 20 Minuten laufen zu lassen, in denen wir sie brauchten, war beim ersten Mal wirklich problematisch. Machbar und in Ordnung, aber plötzlich skalierten wir Dinge, die wir normalerweise nicht skalieren würden.

Was ist mit internen APIs, wir haben gerade über externe gesprochen? Hier kommen wir zu den eigentlichen Herausforderungen, denn davon gibt es immer mehr. Im Allgemeinen stellen Sie der Welt nur einige wenige spezifische APIs vor. Aber intern, sogar in dieser Aktentasche, sah man, wie viele APIs wir tatsächlich hatten. Die offensichtliche Option besteht darin, dass wir wissen, wie man REST macht, und REST intern verwenden. Wir haben Möglichkeiten, es zu spezifizieren, wir können tatsächlich spezifizieren, welche Dinge vor und zurück kommen, und wir können alle diese Systeme miteinander kommunizieren lassen. Das kann funktionieren. Es kann wirklich gut funktionieren, aber Sie verwenden REST-APIs. Dabei handelt es sich wiederum um Representational State Transfers. Du rufst jetzt jedes Mal etwas an. Wenn Sie nicht HTTP/2 verwenden, führen Sie jedes Mal ein vollständiges SYN-ACK durch. Das ist ziemlich schwer und Sie sind jetzt darauf angewiesen, die Anforderungsverkettung in Ihrem System zu blockieren. Möchten Sie wirklich Load Balancer und Caches in Ihrer Firewall haben? Wenn Sie es so betrachten, könnten Ihre externen Dienste ziemlich stark betroffen sein, aber wenn sich Systeme über interne Systeme ausbreiten, könnten Sie 1000 interne Systeme haben, und jeder Systemaufruf von den externen Systemen könnte 50 bis 150 der Dinge erfordern, die sie durchlaufen. Plötzlich verstärkt sich jeder Anruf um das 100-fache oder vielleicht 30-fache oder vielleicht 1000-fache, sodass Sie jetzt möglicherweise Lastausgleichsgruppen für Ihre tatsächlichen internen Dienste benötigen. Ist es das, was du wirklich willst? Sie müssen all diese Dinge miteinander verbinden. Man kann es machen, aber ich empfehle es wirklich nicht. Bei manchen Dingen ist das möglich, bei anderen nicht. Einige Systeme sind dafür ausgelegt. Im Allgemeinen sind Sie in dieser Welt, in der es so ist: Sind Sie sicher, dass Sie die Dinge so eng miteinander verknüpfen wollen?

Reden wir über Pub/Sub. Wir reden hier nicht von Kneipen im maritimen Stil. Hier kommen wir zum Evolutionsschritt darüber, was APIs wirklich geworden sind. Da wir diesen Schwarm von Microservices haben, dieser stammt von Netflix und zeigt die Dinge, die über ihr Web zu all ihren Backend-Diensten laufen, Sie können die Routen sehen, die sie alle verfolgen. Natürlich möchten Sie alles zusammenstecken. Das Zusammenfügen von APIs ist nicht so schlimm, außer dass das Problem darin besteht, dass Sie dies vereinheitlichen möchten, sodass Sie wissen, dass viele Dinge mit derselben Technologie kommunizieren, sodass Sie nicht 100 verschiedene Teile warten müssen. Sie können sich für das Pub/Sub-Muster entscheiden. Das Pub/Sub-Muster ist im Grunde nur eine gute Möglichkeit, Dinge zu entkoppeln. Es gibt die Idee eines Herausgebers, eines Maklers und eines Abonnenten.

Es gibt verschiedene Möglichkeiten, dies in verschiedenen Technologien zu tun. Das coole Kernkonzept dabei ist, dass man die Kunst, etwas geschehen zu lassen, von der Kunst, ihm zuzuhören, entkoppelt. Insbesondere bei Dingen wie Microservices bedeutet das, dass man etwas veröffentlicht, das heißt, einen Job macht, eine Aktion ausführt, etwas so macht. Beliebig viele Systeme können es anschließend aufnehmen, ohne dass eine Abhängigkeit davon besteht, dass das erste System noch am Leben ist. Sie koppeln diese Systeme lose mit einem internen System, das speziell auf Robustheit ausgelegt ist. Gängige Muster sind hier RabbitMQ, ein schönes Beispiel dafür, dass Sie nie über Version 0.9 hinausgehen müssen, um erfolgreich zu sein. NATS und JetStream und solche Dinge sind ein schönes, unterhaltsames CNCF-Projekt, das zeigt, dass man, wenn man Dinge oft genug umschreibt, irgendwann etwas ziemlich Cooles bekommt, da es ursprünglich als sehr „Fire and Forget“-Dienst in Ruby begann und es auch jetzt ist fast das gleiche wie Pulsar. Pulsar, ein weiteres erstklassiges Apache-Projekt, ist der logische Endpunkt dessen, worum es bei diesen großen Streaming-Systemen geht, mit mehr verteilten Systemen als der Rest Ihres Netzwerks zusammen, da es drei und nicht eines ist. Und schließlich haben wir noch Kafka. Es ist diese Art von Inside-Out-Denken Ihrer Datenbank. Es ist stark schreiboptimiert, so konzipiert, dass es vom ersten Tag an robust ist, und relativ einfach, was jedoch darüber hinwegtäuscht, wie viel Sie damit machen können.

Dann stellt sich die Frage: Ist es an der Zeit, mit dem Streaming zu beginnen? Da wir hier angelangt sind, haben wir nun diese geordneten Systeme aufgebaut, die Nachrichten verschicken, sie befinden sich in Protokollen oder Warteschlangen. Ist es an der Zeit, mit der Stream-Verarbeitung zu beginnen? Lassen Sie uns in diesem Zusammenhang eine Geschichte erzählen. Das ist weit zurück aus meiner Zeit als CTO. Nehmen wir an, Sie erstellen eine SaaS-Plattform, möglicherweise für Lieferung und Logistik, wie eine der Plattformen, die ich zuvor erstellt habe. Was dann passiert, ist, dass Sie über ein lose verbundenes Netzwerk von Microservices verfügen, das Warteschlangen und Streams verwendet. In diesem Fall hatten wir eine sehr große rechenintensive Aufgabe, nämlich Routen zu berechnen. Damit wäre das Problem der vollständigen Fahrzeugführung gelöst. Es handelt sich um ein NP-schweres Problem. Sie können sich vorstellen, wie viel CPU-Belastung das tatsächlich erforderte. Wir hätten es auf eine GPU übertragen können, aber das hätte damals viel mehr Erfahrung erfordert, aber auch eine Menge Code erfordert, der uns einfach nicht zur Verfügung stand. Wir verfügten über eine Reihe sehr großer CPU-intensiver Maschinen und hatten die Möglichkeit, Arbeit aus einer Arbeitswarteschlange abzurufen. Ursprünglich haben wir versucht, dies mit einigen anderen existierenden Systemen zu tun. Am Ende haben wir semi-benutzerdefinierte Warteschlangeninhalte mit angehängten Protokollen erhalten, weil wir die Idee eines fairen Lastausgleichs in Kombination mit der Stream-Verarbeitung umsetzen wollten. Das hat super funktioniert. Wir sind von einem System, das REST-betrieben war und bei dem es zu unkontrollierten Ausbrüchen unbekannter Arbeitsmenge kam, zu einem System übergegangen, das den Durchsatz wirklich bewältigte. Das ließ sich großartig skalieren, bis das nicht mehr der Fall war.

Was passiert ist, war, dass wir Kunden ziemlich erfolgreich eingebunden haben. Während wir sie eingebunden haben, wussten wir nicht, dass sie hinter den Kulissen ziemlich fragmentiert waren. Wir sind auf das Problem gestoßen, dass es sich um eine JSON-Datei handelt und eine JSON-Datei nicht unbedingt dasselbe ist. Man hat Dimensionen in Masse. Eines hat Abmessungen in Zentimetern. Da ist ein Tippfehler drin. Sie gelangen zu diesem einfachen Problem, bei dem alles, was vorgelagert ist und REST ist, nachgelagert in Streaming-Systeme gelangt, die für rechenintensive Arbeitslasten ausgelegt sind, und sie sind nicht gleich. Dies führte dazu, dass über unsere APIs eine Reihe von Giftpillen-Meldungen flossen, von denen wir nicht vollständig wussten. Später haben wir etwas daran gearbeitet, tatsächlich mehr Verifizierung im Upstream einzubauen. Das Problem bestand wiederum darin, dass die Leute eine API nutzen werden, sobald sie der Welt zugänglich gemacht wird. Das bedeutet, dass die Menschen darauf angewiesen sein werden. Oft haben sie nicht einmal die volle Kontrolle darüber, was sich sonst noch in ihrem eigenen System befindet. In diesem Fall waren diese Objekte für sie im Grunde nur große binäre BLOBs, da sie von anderen Drittsystemen stammten. Es wird in Ihr magisches Prozessnetzwerk fallen, um zu versuchen, es zu lösen.

Was wirklich schlimm wurde, war, dass wir im Grunde noch nie zuvor einen Artikel hatten, der in diesem speziellen Fall ausgefallen ist, sodass er an verschiedenen Stellen auf dem Weg unzustellbar war, und ein Warteschlangensystem für unzustellbare Briefe war wie folgt eine echte Botschaft? Es würde gehen, ja, das ist eine echte Botschaft, und sie wird in verschiedenen Phasen immer wieder in das System zurückkehren. Letztendlich hatten wir das Problem, dass es aus dem Ende von Warteschlangen für unzustellbare Nachrichten herausfiel, die eigentlich nie Warteschlangen für unzustellbare Nachrichten sein sollten. Weil irgendetwas es nicht gut genug lesen konnte, um es wieder an die richtige Stelle zu bringen oder es überhaupt zu protokollieren. Noch einmal: Das sind die Dinge, die man lernt, wenn man erkennt, dass eine uneingeschränkte API-Oberfläche wirklich gefährlich ist, aber selbst eine relativ eingeschränkte API-Oberfläche geht nicht durchgehend von denselben Annahmen aus. Da wir Systeme in Python hatten, die im Allgemeinen ein bisschen so aussehen könnten, wird das wahrscheinlich funktionieren, und es gab in Clojure immer noch Dinge, die nur dazu gedacht waren, Dinge zu verarbeiten und Nachrichten zu interpretieren, auf die sie stießen. Als sie dann einige der C++-Systeme im Backend trafen, hieß es: Ja, nein, das lehnen wir ab. Entweder lösen wir eine Ausnahme aus und stürzen Ihren Server ab, oder wir verwerfen ihn einfach. Wir kamen zu der einfachen Tatsache, dass wir die Impedanzen in unserem Netzwerk nicht anpassen konnten.

Hier wollten wir Spezifikationen. Sie fragen sich: Was können wir dagegen tun, denn API-Spezifikationen sind zwar schön und gut, schützen uns aber nur bis zu einem gewissen Grad? Denn das meiste, was eingesandt wird, ist JSON und das muss so sein, um die Flexibilität für die Außenwelt aufrechtzuerhalten. Sie wollen Schemata. Das ist das Erste: Was ist eine Nachricht? Das JSON-Schema oben soll im Grunde nur sagen, dass wir zumindest wissen, was diese Felder sind. Wenn Sie darüber nachdenken, was ist eine JSON-Datei? Es ist Text. Wie viel Präzision steckt in einem Float? Ist es ein Doppelgänger? Die Antwort ist, dass Sie es nicht wissen. Am Ende kommt es auch zu vielen Dingen, bei denen nicht jeder JSON formal auf die gleiche Weise verwendet. Sogar eine Verifizierung könnte einen gewissen Zwang hervorrufen, von dem Sie einfach nichts wissen können, es sei denn, Sie sind die ganze Zeit über genau derselbe, insbesondere wenn Sie anfangen, verschiedene Deserialisierer zu verwenden. Man kann nie ganz sicher sein, was als nächstes passieren wird.

Dann kommen wir zu den nächsten Optionen hier, nämlich der Idee von Protobuf oder Protokollpuffern von Google und Avro von der Apache Foundation. Protokollpuffer, die ursprünglich für die gRPC-Nutzung gedacht waren, sind die klassischen schemagesteuerten Protokollpuffer. Wir generieren Code für Sie und es funktioniert einfach. Tun sie. Sie erfreuen sich großer Beliebtheit, weil gRPC unglaublich nützlich ist. Allerdings sind sie nicht kostenlos. Ich habe an einem Ort gearbeitet, an dem wir sehr schnelle Nachrichten hatten, die alle in Protobuf kodiert waren und innerhalb einer einstelligen Millisekundenzahl dekodiert werden mussten. Sie müssen stapelweise dekodiert werden, wobei es verschiedene Schemata geben kann. Was man dort ziemlich schnell findet, ist die tatsächliche Implementierung des Protokollpuffersystems hinter dem Boden, wo es die Zuweisungen vornimmt, um Objekte im neuen Format anzuordnen, und dieses Thunk-Unthunk-Ding ist ziemlich problematisch und verursacht eine massive Heap-Fragmentierung, weil es einfach ist nicht für hohe Geschwindigkeit ausgelegt. gRPCs sind großartig, aber sie sind noch viel mehr: Etwas tun, verschwinden, fertig. Gelegentlich wird ein paar genau das Gleiche hintereinander gestreamt, und das ist in Ordnung, also halten die Arena-Zuteilungen stand.

Wenn Sie schneller arbeiten und versuchen, das System ein wenig zu belasten, werden Sie schnell feststellen, dass es Zuweisungen pro Artikel vornimmt. Ich meine nicht nur gegen einen Pool, ich meine, es reicht auch in einer eigenen Arena. Es wird nicht einmal dazu kommen, Jemalloc zu machen, das Ding vom Typ Jemalloc ist das, wo es hingehen wird. Dann wird es das Ding dort machen, wo es hingehört. Ich habe bereits den Pool, keine Sorge, oder sogar Snmalloc, wenn Sie schlau sind. Das wird einfach nicht passieren, weil es sich um eine benutzerdefinierte Arena handelt, also wird es Ihre Arena nur ruinieren. Apache Avro ist eine ähnliche Sache, konzentriert sich jedoch im Vergleich zu früher stärker auf diese Art von Modell für das Evolutionsmodell. Wieder einmal sehr ähnlich im Betrieb, viel geringer in der tatsächlichen Datenlast über die Leitung, etwas langsamer beim Kodieren und Dekodieren. Ich bin ein großer Fan dieses Protokolls, weil es die Idee der Präzision wirklich gut versteht und eine erstaunliche Interoperabilität mit den meisten Teilen des Kafka-Ökosystems aufweist. Ja, ich arbeite intensiv mit Kafka. Der kleine Fehler besteht darin, dass die Leute darüber hinaus Dinge implementiert haben. Es gibt tatsächlich ein paar verschiedene Avro-Geschmacksrichtungen, und das kann zu Problemen führen. Insbesondere bei Kafka werden bestimmten Dingen in Schema-Registern einige magische Bits hinzugefügt, die Ihnen das Leben schwer machen.

Auf der anderen Seite haben wir binäre, aber benutzerfreundlichere Formate. FlatBuffers ist eine logische Weiterentwicklung von Protokollpuffern. Ich empfehle Ihnen dringend, sich diese anzusehen, wenn Sie ein Fan von Protokollpuffern sind. FlatBuffers sind direkt so konzipiert, dass sie viel besser in ihre Endbenutzerformate kodieren und dekodieren. Sie wurden aus der Idee heraus entwickelt, Protokollpuffer für Spiele nutzbar zu machen. Für Spiele gelten wesentlich höhere Bandbreiten- und Latenzanforderungen als für die meisten SaaS-Programme. Dann gibt es noch MessagePack, was im Grunde bedeutet, dass wir es JSON wollen, aber wir wollen es binär. Es ist in der Tat wunderschön, da es schemalos ist, aber dennoch sehr flexibel und oft kleiner als ein Protobuf-Drahtpaket. Dennoch haben wir immer noch unendliche Flexibilität, da diese Binärdatei tatsächlich die Bitausrichtung beibehält und Sie am anderen Ende die Präzision eines Objekts beibehalten. Ohne ein Schema benötigen Sie jedoch noch etwas anderes, um eine formelle Überprüfung bereitzustellen. Sie gehen hier einen großen Kompromiss ein, indem Sie sagen, dass ich möglicherweise immer noch ein JSON-Schema benötige, das zu meinem MessagePack passt. Ich tendiere stark zum Ansatz der Verwendung von Avro und/oder MessagePack. Ich rate in den meisten Fällen dringend von Protobufs ab und sage FlatBuffers als die bessere Version davon. Andere Systeme wie Cap'n Proto sind großartig. Sie arbeiten. Ich habe keine feste Meinung dazu, weil ich sie nie umsetzen musste.

Wenn wir uns dann mit diesem Thema befassen, gibt es tatsächlich Antworten. Die Linux Foundation hat kürzlich, in den letzten drei Jahren, ein Projekt namens AsyncAPI ins Leben gerufen, das der Idee ähnelt, eine Mischung aus OpenAPI-Systemen wie RAML und anderen ähnlichen Dingen zu verwenden, um im Grunde festzulegen, was eine asynchrone API ist, Dinge wie MQTT , Dinge wie WebSockets, Dinge wie Kafka. Dies sind Systeme, die nicht nur „feuern und vergessen“ machen. Stattdessen werden offene Streams häufig auf langlebigere Verbindungen portiert. Die Spezifikation ist tatsächlich recht detailliert, da sie zusätzlich zu den Endpunkten auch angibt, was ein Broker ist, sodass Sie tatsächlich mehr darüber wissen, was vor sich geht. Außerdem ist natürlich auch ein JSON-Schema angehängt, sodass Sie Dinge spezifizieren können. Da es sich um ein JSON-Schema handelt, können Sie es mit der richtigen Erweiterungsstufe ganz einfach in andere Typen übersetzen. Mittlerweile tauchen Tools wie SpecMesh auf, die für Verbraucher dieser Dinge wirklich nützlich sind, um Ihr System quasi als Datenkatalog zu spezifizieren und zu sagen: „Das ist mein Datenkatalog. Das ist die Spezifikation dafür. So geht’s.“ Ich greife darauf zu. Andere Tools wie das Datenkatalog-Tool von LinkedIn gehen nicht ganz so weit. Dinge wie Aivens eigenes internes Tool namens Klaw, das eine Möglichkeit zur Verwaltung von Kafka bietet, mit Dingen wie Karapace, gehen immer noch nicht ganz so weit wie SpecMesh. Sie tendieren alle zum selben Ziel: Wir brauchen Spezifikationen, die alle Arten von Ergebnissen abdecken. Es macht wirklich Spaß zu sehen, wie sich dieses Ding vor meinen Augen entwickelt. Ich habe sie kürzlich zum ersten Mal bei einem Treffen getroffen und dachte: Das ist verrückt. Warum hat das noch niemand gemacht? Das ist die beste Überlegung, denn das bedeutet, dass Sie es nicht bauen müssen. Diese sind natürlich alle vollständig Open Source.

Die eigentliche Frage, die sich dabei stellt, lautet: Sollten APIs eigene Dienste sein? Da wir uns in dieser verteilten Welt befinden, haben wir viele verschiedene Geräte, die alle miteinander verbunden sind. Warum sind APIs keine eigenständigen APIs? Das ist tatsächlich eine Sache, die immer wieder auftaucht, denn wenn man darüber nachdenkt, ist es so: Ich möchte Ihnen die Frage stellen: Wiederholen wir uns? Da wir jetzt diese Dinge haben, die reden müssen, muss ihnen klar gemacht werden, wie sie reden. Dann müssen wir definieren, was sie an diesen Rändern tun. Die eigentliche Frage, die wir hier haben, ist, schauen Sie sich das Wort selbst an, das Akronym APIs, das eine Anwendungsprogrammschnittstelle bezeichnet. Wenn wir uns über Wiederholungen Gedanken machen, schauen wir uns das an und denken: Wenn eine API nur eine Schnittstelle ist, sind Microservices Objekte, und dann denken wir, wir haben das schon einmal gemacht, was wunderbar ist, weil es bedeutet, dass man sich Dinge ausleihen kann. Sie denken über die SOLID-Prinzipien des objektorientierten Designs nach. Das Ganze basiert auf dem Prinzip, dass wir im Grunde das objektorientierte Design nachgebildet haben, von Beobachtermustern mit Streaming-Microservices bis hin zu SOLID-Designprinzipien, wenn es um das eigentliche API-Design selbst geht. Das Wichtigste, was Sie jedoch wirklich tun müssen, ist, dass Sie sich SOLID ansehen und überlegen: Wie funktioniert diese Karte? Wirklich ganz nah dran. Wir haben die Idee der Einzelverantwortung. Diese API macht eine Sache, und zwar könnte sie meine anderen APIs der Welt zugänglich machen. Möglicherweise verwalte ich einen OpenSearch-Cluster. Vielleicht kümmere ich mich um die Gehaltsabrechnung.

Dann ist das Offen- und Geschlossen-Prinzip tatsächlich die Idee der Versionierung. Das ist eine ganz, ganz große Sache bei APIs. Sie müssen offen dafür sein, neue Versionen und neue Dinge hinzuzufügen, aber Sie können niemals Dinge verwerfen, auf denen tatsächliche Dinge basieren, daher das Offen/Geschlossen-Prinzip. Wir werden die Substitutionssache aus dem wichtigen Grund ignorieren, dass dies hier nicht zutrifft. Dies ist einer der wenigen Bereiche des SOLID-Designs, von denen ich kein großer Fan bin, denn obwohl es sich um Objekte handelt, existiert das Konzept der Vererbung grundsätzlich nicht. Sie können Teile von anderen APIs wie die Authentifizierung erben, Ihre API ist jedoch spezifisch für ihre Rolle. Das ist der einzige Teil, der nicht zutrifft. Wenn wir dann weiter auf die Sache mit der Schnittstellentrennung eingehen, ist das die Idee der externen Schnittstelle, bei der Ihre API die externe Schnittstelle für das System selbst ist. Hier legen Sie lediglich offen, was sein soll, und nicht, was diese internen Details alle bewirken. Bei der Idee der Abhängigkeitsumkehr geht es eigentlich darum, ein erstklassiger Bürger in Ihrem System zu sein, denn Sie haben eine Spezifikation und leben damit, denn wenn Sie dies nicht tun, haben Sie diese eng gekoppelten Dienste, die die tatsächlichen sind API ist eng mit seinem Inneren verbunden, und es gibt jetzt diese Möglichkeit, es zu trennen. Das ist wie eine Schnittstelle. Der Grund, warum Sie Schnittstellen verwenden, besteht darin, den Zugriffsmechanismus von den eigentlichen Dingen hinter den Kulissen zu entkoppeln, indem die Abhängigkeit zur Schnittstelle und nicht zur tatsächlichen Implementierung umgekehrt wird.

Wenn Sie all diese Dinge ein wenig umformulieren und darüber nachdenken, was haben wir sonst noch? Wir haben das, was ich die Philosophie des SAFE API-Designs nenne: Einzelverantwortung, API-Versionierung, erstklassige Spezifikationen und eine externe Schnittstelle. Die Reihenfolge ist nicht ganz perfekt, aber das Wort sieht gut aus. Wie bei den besten Akronymen überhaupt, lande ich hier. In einem verteilten System ist dies sogar noch wichtiger, denn wenn Sie verschiedene Blöcke aufbauen, spielt es keine Rolle, wie viele Sie hinzufügen, solange sie nur eine Aufgabe erfüllen. Sie sollten nie wirklich zwei verschiedene APIs für Zahlungen haben, es sei denn, diese unterscheiden sich wesentlich. Sie müssen Ihre APIs versionieren. Sie müssen über erstklassige Spezifikationen verfügen, wenn Sie tatsächlich maßstabsgetreue Dinge herstellen möchten, ohne dass die Dinge explodieren. Sie sollten wirklich eine externe Schnittstelle entwerfen, damit Ihre API nutzbar ist. Wenn wir zusätzliche Ebenen hinzufügen, wie Beobachterprinzipien und alles andere, was wir aus dem Softwaredesign gelernt haben, und API-Blöcke so behandeln, als wären sie nur Objekte in Ihrem normalen Code, wird Ihnen klar, dass es viele weitere Möglichkeiten gibt, dies zu erweitern viele weitere Briefe. Um ein schönes Bild zu erhalten, lasse ich es so wie es ist. Die nächste Frage, die Sie stellen werden, ist: Haben Sie sich das schon einmal angesehen und denken darüber nach, es zu wiederholen? Werfen wir einfach noch einen rein. Wenn Sie APIs als Schnittstellen und Mikroservices als Objekte betrachten, gehe ich davon aus, dass AWS Lambda und alle Funktionen und Dienste Monaden sind.

Weitere Präsentationen mit Transkripten ansehen

Aufgenommen am:

30. August 2023

von

Ben Gamble

Identitäten schützen. Sichere digitale Dienste. Ermöglichen Sie einen skalierbaren und sicheren Benutzerzugriff auf Web- und mobile Anwendungen. Kostenlos testen.

Weitere Präsentationen mit Transkripten ansehen
AKTIE