Einige Unternehmen, darunter auch unser Kunde, entwickeln das Produkt über ein Partnernetzwerk. Zum Beispiel sind große Online-Shops mit einem Lieferdienst integriert – Sie bestellen einen Artikel und erhalten bald darauf eine Sendungsverfolgungsnummer. Ein weiteres Beispiel – zusammen mit dem Flugticket kaufen Sie eine Versicherung oder ein Ticket für den Flughafenexpress.
Hierfür wird eine API verwendet, die über das API-Gateway an Partner ausgegeben werden muss. Diese Aufgabe haben wir gelöst. In diesem Artikel werden wir die Einzelheiten erläutern.
Gegeben: ein Ökosystem und ein API-Portal mit einer Schnittstelle, in der die Benutzer registriert sind, Informationen erhalten usw. Wir müssen ein benutzerfreundliches und zuverlässiges API-Gateway erstellen. Dabei mussten wir sicherstellen,
- die Registrierung,
- die Kontrolle des Zugriffs auf die API,
- die Überwachung, wie Benutzer das Endsystem nutzen,
- die Erfassung von Geschäftszahlen.

In diesem Artikel werden wir von unseren Erfahrungen bei der Erstellung eines API-Gateways berichten, während wir die folgenden Aufgaben gelöst haben:
- Benutzer-Authentifizierung,
- Benutzer-Autorisierung,
- Modifikation der ursprünglichen Anfrage,
- Proxying der Anfrage,
- Nachbearbeitung der Antwort.
Es gibt zwei Arten des API-Managements:
1. Standard, der folgendermaßen funktioniert. Bevor der Benutzer sich verbindet, testet er die Möglichkeiten, zahlt dann und integriert es auf seiner Website. Dieser wird häufig im kleinen und mittleren Unternehmen genutzt.
2. Großes B2B API-Management, bei dem das Unternehmen zunächst eine geschäftliche Entscheidung über die Verbindung trifft, Partner des Unternehmens mit vertraglichen Verpflichtungen wird, und sich dann mit der API verbindet. Erst nach Klärung aller Formalitäten erhält das Unternehmen Testzugang, durchläuft Tests und geht live. Doch dies ist ohne eine Managemententscheidung über die Verbindung nicht möglich.

Unsere Lösung
In diesem Abschnitt werden wir über die Erstellung des API-Gateways berichten.
Die Endbenutzer des erstellten API-Gateways sind die Partner unseres Kunden. Für jeden von ihnen haben wir bereits die erforderlichen Verträge gespeichert. Wir müssen nur die Funktionalität erweitern, indem wir den bereitgestellten Zugang zum Gateway kennzeichnen. Dementsprechend ist ein kontrollierter Verbindungs- und Verwaltungsprozess erforderlich.
Natürlich hätten wir eine vorhandene Lösung für das API-Management und insbesondere für die Erstellung eines API-Gateways verwenden können. Ein Beispiel dafür könnte sein. Es passte nicht zu uns, da wir bereits ein API-Portal und ein riesiges Ökosystem darum herum hatten. Alle Benutzer waren bereits registriert und wussten, wo und wie sie die benötigten Informationen erhalten konnten. Im API-Portal gab es bereits die erforderlichen Schnittstellen, wir benötigten nur einen API Gateway. Tatsächlich haben wir uns mit dessen Entwicklung beschäftigt.
Was wir API Gateway nennen, ist eine Art Proxy. Hier hatten wir wieder eine Wahl — wir konnten unseren eigenen Proxy schreiben oder etwas Fertiges wählen. In diesem Fall haben wir den zweiten Weg gewählt und uns für die Kombination nginx+Lua entschieden. Warum? Wir benötigten zuverlässige, getestete Software, die Skalierung unterstützt. Nach der Implementierung wollten wir nicht die Geschäftsanfragenvalidität und die Funktionsfähigkeit des Proxys überprüfen.
Jeder Webserver hat eine Anfrageverarbeitungspipeline. Im Fall von nginx sieht sie wie folgt aus:

(Diagramm aus )
Unser Ziel war es, uns in diese Pipeline an dem Punkt einzufügen, an dem wir die ursprüngliche Anfrage modifizieren können.
Wir wollen einen transparenten Proxy erstellen, sodass die Funktionalität der Anfrage so bleibt, wie sie angekommen ist. Wir kontrollieren nur den Zugriff auf die endgültige API und helfen der Anfrage, sie zu erreichen. Im Falle einer ungültigen Anfrage sollte die endgültige API den Fehler anzeigen, nicht wir. Der einzige Grund, aus dem wir eine Anfrage ablehnen können, ist der fehlende Zugang des Clients.
Für nginx gibt es bereits ein auf . Lua ist eine Skriptsprache, die sehr leichtgewichtig und einfach zu erlernen ist. Somit haben wir die notwendige Logik mit Lua umgesetzt.
Die Konfiguration von nginx (analoge Route der Anwendung), wo die gesamte Arbeit verrichtet wird, ist ziemlich verständlich. Bemerkenswert ist hier die letzte Direktive — post_action.
location /middleware {
more_clear_input_headers Accept-Encoding;
lua_need_request_body on;
rewrite_by_lua_file 'middleware/rewrite.lua';
access_by_lua_file 'middleware/access.lua';
proxy_pass https://someurl.com;
body_filter_by_lua_file 'middleware/body_filter.lua';
post_action /process_session;
}
Lassen Sie uns betrachten, was in dieser Konfiguration geschieht:
more_clear_input_headers — entfernt den Wert der nach der Direktive angegebenen Header.
lua_need_request_body — regelt, ob der ursprüngliche Anfragekörper gelesen werden soll, bevor die Direktiven rewrite/access/access_by_lua ausgeführt werden. Standardmäßig liest nginx den Anfragekörper des Clients nicht, und wenn Sie darauf zugreifen müssen, muss diese Direktive den Wert on haben.
rewrite_by_lua_file — Pfad zum Skript, in dem die Logik zur Modifikation der Anfrage beschrieben ist
access_by_lua_file — Pfad zum Skript, in dem die Logik zur Überprüfung des Zugriffs auf die Ressource beschrieben ist.
proxy_pass — URL, an die die Anfrage weitergeleitet wird.
body_filter_by_lua_file — Pfad zum Skript, in dem die Logik zur Filterung der Anfrage vor der Rückgabe an den Client beschrieben ist.
Und schließlich, post_action — offiziell nicht dokumentierte Direktive, mit der weitere Aktionen ausgeführt werden können, nachdem die Antwort an den Client gegeben wurde.
Im Folgenden erklären wir der Reihe nach, wie wir unsere Aufgaben gelöst haben.
Autorisierung/Authentifizierung und Modifikation der Anfrage
Autorisierung
Die Autorisierung und Authentifizierung haben wir mithilfe von Zugängen über Zertifikate umgesetzt. Es gibt ein Root-Zertifikat. Für jeden neuen Kunden des Auftraggebers wird ein persönliches Zertifikat generiert, mit dem er Zugang zur API erhalten kann. Dieses Zertifikat wird im Abschnitt server der Nginx-Einstellungen konfiguriert.
ssl on;
ssl_certificate /usr/local/openresty/nginx/ssl/cert.pem;
ssl_certificate_key /usr/local/openresty/nginx/ssl/cert.pem;
ssl_client_certificate /usr/local/openresty/nginx/ssl/ca.crt;
ssl_verify_client on;Modifikation
Es könnte die berechtigte Frage aufkommen: Was tun wir mit einem zertifizierten Kunden, wenn wir ihn plötzlich vom System abmelden wollen? Sollen wir alle anderen Kunden zwingen, neue Zertifikate zu beantragen?
So sind wir sanft zur nächsten Aufgabe übergegangen — der Modifikation der ursprünglichen Anfrage. Die ursprüngliche Anfrage des Clients ist, gelinde gesagt, nicht gültig für das Zielsystem. Eine der Aufgaben besteht darin, fehlende Teile in die Anfrage einzufügen, um sie gültig zu machen. Der Clou ist, dass die fehlenden Daten für jeden Client unterschiedlich sind. Wir wissen, dass der Client mit einem Zertifikat zu uns kommt, von dem wir den Fingerabdruck nehmen und die erforderlichen Daten des Kunden aus der Datenbank abrufen können.
Falls zu einem späteren Zeitpunkt der Bedarf besteht, den Kunden von unserem Service abzumelden, verschwinden seine Daten aus der Datenbank und er kann nichts mehr tun.
Arbeiten mit den Kundendaten
Wir mussten eine hohe Verfügbarkeit der Lösung sicherstellen, insbesondere dafür, wie wir die Kundendaten abrufen. Die Schwierigkeit besteht darin, dass die Quelle dieser Daten ein externer Dienst ist, der keine durchgängige und ausreichend hohe Geschwindigkeit garantiert.
Daher mussten wir eine hohe Verfügbarkeit der Kundendaten sicherstellen. Als Werkzeug haben wir , das uns Folgendes bietet:
- schneller Zugriff auf Daten,
- die Möglichkeit, einen Cluster aus mehreren Knoten mit replizierten Daten auf verschiedenen Knoten zu organisieren.
Wir haben die einfachste Strategie zur Datenübertragung in den Cache gewählt:

Die Arbeit mit dem Endsystem erfolgt im Rahmen von Sitzungen, und es gibt eine Begrenzung für die maximale Anzahl. Wenn der Kunde die Sitzung jedoch nicht geschlossen hat, müssen wir dies tun.
Die Daten zur offenen Sitzung kommen vom Endsystem und werden zunächst auf der Lua-Seite verarbeitet. Wir haben uns entschieden, Hazelcast zu verwenden, um diese Daten mit einem Job, der in .NET geschrieben wurde, zu speichern. Dann überprüfen wir in bestimmten Abständen das Lebensrecht der offenen Sitzungen und schließen abgelaufene.
Zugriff auf Hazelcast sowohl aus Lua als auch aus .NET
Es gibt keine Lua-Clients für die Arbeit mit Hazelcast, aber Hazelcast hat eine REST-API, die wir nutzen wollten. Für .NET gibt es jedoch , über den wir planen, Zugang zu den Hazelcast-Daten auf der .NET-Seite zu erhalten. Doch es kam anders.

Beim Speichern von Daten über REST und Abrufen über den .NET-Client werden unterschiedliche Serializer/Deserializer verwendet. Daher ist es unmöglich, Daten über REST zu speichern und über den .NET-Client abzurufen und umgekehrt.
Wenn es Interessierte gibt, werden wir in einem separaten Artikel näher auf dieses Problem eingehen. Spoiler — im Diagramm.

Protokollierung und Monitoring
Unser Unternehmensstandard für Protokollierung über .NET ist Serilog, alle Protokolle landen letztendlich in Elasticsearch, die Analyse erfolgt über Kibana. Etwas Ähnliches wollten wir auch in diesem Fall machen. Leider fand der einzige Client für die Arbeit mit Elastic auf Lua bei der ersten require-Anweisung nicht statt. Und wir haben Fluentd verwendet.
ist eine Open-Source-Lösung zur Bereitstellung einer einheitlichen Protokollierungsschicht der Anwendung. Sie ermöglicht das Sammeln von Protokollen aus verschiedenen Schichten der Anwendung und deren Übertragung an eine einheitliche Quelle.
API Gateway läuft in K8S, daher haben wir entschieden, einen Container mit Fluentd in dasselbe Pod zu integrieren, um Protokolle in den vorhandenen offenen TCP-Port von Fluentd zu schreiben.
Wir haben auch untersucht, wie sich Fluentd verhält, wenn es keine Verbindung zu Elasticsearch hat. Über zwei Tage lang gingen kontinuierlich Anfragen an das Gateway, Protokolle wurden an Fluentd gesendet, aber die IP von Elastic war für Fluentd gesperrt. Nach Wiederherstellung der Verbindung hat Fluentd alle Protokolle erfolgreich an Elastic übertragen.
Fazit
Der gewählte Implementierungsansatz ermöglichte es uns, ein funktionierendes Produkt in einer Produktionsumgebung in nur 2,5 Monaten bereitzustellen.
Wenn Sie jemals mit solchen Dingen zu tun haben, empfehlen wir Ihnen, zuerst genau zu verstehen, welche Aufgabe Sie lösen und welche Ressourcen Ihnen bereits zur Verfügung stehen. Achten Sie auf die Herausforderungen bei der Integration mit bestehenden API-Management-Systemen.
Verstehen Sie für sich selbst, was genau Sie entwickeln möchten – nur die Geschäftslogik zur Verarbeitung von Anfragen oder, wie es in unserem Fall der Fall sein könnte, das gesamte Proxy. Vergessen Sie nicht, dass alles, was Sie selbst tun, anschließend gründlich getestet werden muss.
Quelle: habr.com
