Das Wichtigste zuerst: connexion ist ein API-first Framework, das von Zalando entwickelt wurde. Während es ursprünglich auf Flask aufbaute, wurde connexion später erweitert, um mit anderen (asynchronen) Web-Frameworks wie aiohttp zu arbeiten.
Mit Connexion schreiben Sie zunächst Ihren API-Vertrag unter Verwendung des Swagger/OpenAPI-Standards. Anschließend werden die von Ihnen definierten Endpunkte auf Ihre Python-Ansichtsfunktionen abgebildet, um sicherzustellen, dass Ihr Python-Code das tut, was Ihr API-Vertrag vorsieht.
Das macht es in der Landschaft der Python-Web-Frameworks ziemlich einzigartig, da die meisten anderen Tools von Ihrem Code ausgehen und nicht umgekehrt.
API-first (oder contract-first) bedeutet, dass die Entwicklung Ihrer REST-API außerhalb Ihres (Python-)Codes beginnt. Sie definieren zunächst das Verhalten Ihrer API mithilfe einer Standardspezifikationssprache (in diesem Fall Swagger/OpenAPI). Anschließend können Sie sich mit den Beteiligten und Ihren API-Kunden beraten, bevor Sie mit der eigentlichen Implementierung beginnen.
Dies steht im Gegensatz zum "code-first"-Ansatz (auch "implementation-first" genannt), bei dem Sie zuerst über die Implementierung nachdenken und diese schreiben. Anschließend können Sie die Dokumentation Ihrer API unter Verwendung des OpenAPI-Standards erstellen.
Bei ML6 arbeiten wir gerne mit dem API-first-Ansatz mit unseren Kunden zusammen. So können wir uns auf das zu lösende Geschäftsproblem konzentrieren, ohne uns in Implementierungsdetails zu verstricken: Es gibt eine klare Trennung von Definition ("was") und Implementierung ("wie"). Außerdem können mehrere Teams (z. B. Frontend- und Backend-Teams) auf der Grundlage des vereinbarten Vertrags parallel arbeiten. Beim Code-First-Verfahren muss eine Partei warten, bis die andere Partei die Implementierung abgeschlossen hat. Zum Beispiel kann das Frontend-Team das Backend mit Hilfe der API-Spezifikation einfach nachbilden.
Außerdem verwenden die meisten anderen Frameworks Dekoratoren, um Ihre Python-Funktionen auf die Endpunkte abzubilden, was Ihren Code durch die Vermischung von REST-"Logik" mit Ihrer Anwendungslogik in derselben Datei unübersichtlich machen kann. In connexion schreiben Sie nur Ihre Anwendungslogik, und connexion wird Ihre Funktion mit dem entsprechenden Endpunkt aus Ihrer API-Definition verknüpfen.
Ausführlichere Informationen zu den Vorteilen eines API-first-Ansatzes finden Sie in den hervorragenden RESTful API-Leitlinien von Zalando oder in diesem Blogpost von Google.
Ein weiterer Vorteil von API-first ist, dass es sich gut in den Workflow zur Bereitstellung Ihrer API auf der Google Cloud Platform für Tools wie Google Cloud Endpoints und Google Cloud API Gateway integrieren lässt, die die OpenAPI-Spezifikation verwenden. Sie können dann die Dokumentation Ihrer API mit dem Endpoints Developer Portal hosten (unter Verwendung einer anderen IAM und Sicherheit als für Ihre API selbst).
Dies gilt auch für andere Cloud-Anbieter. Zum Beispiel können Sie damit Ihre API auch für Amazon API Gateway oder Azure API Management bereitstellen. Bei der Verwendung eines Code-first-Ansatzes müssen Sie Ihre Anwendung ausführen, um den API-Vertrag zu extrahieren, bevor Sie ihn bereitstellen können, was mühsam sein und zu Problemen führen kann. Beispielsweise entspricht die generierte Spezifikation nicht genau dem, was Sie beabsichtigt haben, oder Ihre Anwendung benötigt eine Datenbankverbindung, die Sie dann nachbilden müssen. Durch die Einführung eines API-First-Frameworks wird der API-Vertrag zur "zentralen Wahrheit", die robuster und sicherer ist, da klarer wird, was die API tun und was sie nicht tun soll und wie die Implementierung möglicherweise davon abweicht.
Schauen wir uns den Unterschied zwischen connexion und einem beliebten, schnell aufstrebenden Web-Framework einmal genauer an: FastAPI. Unser Ziel ist es nicht, zu behaupten, dass das eine unbedingt besser ist als das andere. Wir versuchen vielmehr, die Vor- und Nachteile des Ansatzes der beiden Frameworks zu ermitteln.
Die funktionalen Unterschiede zwischen Connexion und FastAPI ergeben sich aus der unterschiedlichen "Vision": Connexion ist API-first, während FastAPI code-first ist.
*Hinweis: pydantic dient dazu, Daten in Python zu parsen, dies ist nicht dasselbe wie die Validierung von Daten. So kann beispielsweise die Zeichenkette "1" als Ganzzahl mit dem Wert 1 geparst werden, ist aber an sich keine gültige Ganzzahl. Dies könnte zu einem überraschenden Verhalten in Ihrer API führen. Siehe dieses Problem für weitere Hintergrundinformationen.
Weitere Unterschiede (die nichts mit dem API-first/Code-first-Ansatz zu tun haben) sind:
Bisher haben wir über die funktionalen Unterschiede gesprochen. Natürlich wollen wir auch wissen, was das in konkreten Zahlen bedeutet. Zu diesem Zweck haben wir ein kleines Benchmarking durchgeführt, um den Unterschied im Durchsatz und in der Latenz zwischen Connexion (mit Flask) und FastAPI für einen einzelnen Endpunkt einer in Produktion befindlichen Anwendung zu ermitteln.
Es ist wichtig anzumerken, dass wir keinen allgemeinen Benchmark zwischen den beiden Rahmenwerken durchführen wollen. Vielmehr wollen wir prüfen, wo die konkreten Unterschiede für unsere spezielle Anwendung liegen. Sie sollten sich niemals auf externe Benchmarks mit einfachen, synthetischen Beispielen verlassen, sondern stattdessen einen eigenen Benchmark mit Ihrer Anwendung durchführen, wenn Sie verschiedene Optionen evaluieren. In diesem Blogpost werden die Gründe dafür näher erläutert. In unserem Fall muss die API Abfragen gegen eine SQL-Datenbank ausführen und kann daher von asynchronem Code profitieren, um das Blockieren beim Warten auf die Rückgabe der Ergebnisse durch die Datenbank zu vermeiden.
In unserem Fall betrachten wir 2 verschiedene Fälle: den synchronen Fall und den asynchronen Fall. Das Setup für unseren Benchmark besteht aus Gunicorn mit 10 Workern (mit dem Standard-Worker für Flask und uvicorn.workers.UvicornWorker für asynchrone Frameworks). Im asynchronen Fall verwenden wir auch die gevent-Bibliothek, die es ermöglicht, synchronen Code mit Flask zu schreiben und den zugrunde liegenden Code so zu monkeypachen, dass er asynchron wird, ohne dass wir uns explizit um die Ereignisschleife kümmern müssen.
Beachten Sie, dass wir eine Implementierung der Verbindung mit Quart verwenden, die noch nicht vollständig integriert und getestet wurde.
Bei FastAPI zwingen Sie das Framework durch die Verwendung von async def, die Funktion in der Hauptereignisschleife auszuführen. Sie müssen also darauf achten, dass Sie keine blockierenden Funktionen ausführen. Wenn Sie regular def verwenden, wird FastAPI die Funktion in einem separaten ThreadpoolExecutor ausführen, so dass Sie sich in diesem Fall keine Gedanken über Blockierung/Nichtblockierung machen müssen. Als Nebeneffekt erlaubt uns dies auch, synchrones Verhalten mit FastAPI zu "erzwingen" (das ein asynchrones Framework ist).
Ein Blick auf die Zahlen zeigt, dass im synchronen Fall connexion + flask die synchrone FastAPI sowohl beim Durchsatz als auch bei der Latenzzeit übertrifft. Im asynchronen Fall zeigt sich, dass FastAPI besser abschneidet als connexion + quart (Anmerkung: es ist möglich, dass in der Quart-Implementierung für connexion noch einige Optimierungen möglich sind). Die Verwendung von gevent führt jedoch zu noch besseren Ergebnissen und ist der klare Gewinner dieser Übung.
Wir von ML6 tun lieber,als zu reden, und versuchen daher, zu Open Source zu helfen und beizutragen, wenn es möglich ist. Denn wir glauben, dass es immer noch das richtige Werkzeug für uns ist, wenn es gut gepflegt wird.
Eine der großen Änderungen, die wir vorgeschlagen haben und die bereits in connexion integriert wurden, ist die Unterstützung mehrerer Authentifizierungsverfahren in logischer UND-Form, wodurch beispielsweise 2 API-Schlüssel in Verbindung miteinander verwendet werden können.
Außerdem haben wir gerade die notwendigen Genehmigungen erhalten, um neue Versionen auf PyPI zu veröffentlichen, was bisher ein Hindernis war. Die nächsten Schritte umfassen die Festlegung der zukünftigen Richtung und der Roadmap von connexion zusammen mit der Community(https://github.com/zalando/connexion/issues/1395). Die große Frage für die Zukunft von connexion ist, wo es den größten Wert für seine Nutzer schafft: als Framework oder eher als Middleware. Wir freuen uns über jeden Beitrag zu dieser Frage, da sie für alle (potenziellen) Nutzer von connexion wichtig ist. Zögern Sie also nicht, Ihre Gedanken auf Github zu hinterlassen.