Einführung
Flask ist ein leichtgewichtiges und flexibles Web-Framework zur Entwicklung kleiner bis mittelgroßer Anwendungen. Es wird häufig in Projekten eingesetzt, die von einfachen persönlichen Blogs bis hin zu komplexeren Anwendungen wie REST-APIs, SaaS-Plattformen, E-Commerce-Websites und datengetriebenen Dashboards reichen.
Mit zunehmendem Datenverkehr oder steigender Komplexität Ihrer Anwendung können jedoch Leistungsengpässe auftreten. Unabhängig davon, ob Sie ein Content-Management-System (CMS), eine API für eine mobile App oder ein Echtzeit-Datenvisualisierungstool entwickeln: Die Optimierung der Flask-Performance ist entscheidend für eine reaktionsschnelle und skalierbare Benutzererfahrung.
In diesem Tutorial lernen Sie Techniken und bewährte Methoden zur Optimierung der Leistung einer Flask-Anwendung kennen.
Voraussetzungen
- Sie benötigen einen Server mit Ubuntu und einen Benutzer ohne Root-Rechte, aber mit sudo-Berechtigung sowie aktivierter Firewall. Anweisungen zur Einrichtung finden Sie in unserer Anleitung „Erste Schritte mit einem Server“. Wählen Sie dazu bitte Ihre Ubuntu-Distribution aus dieser Liste aus. Stellen Sie sicher, dass Sie eine unterstützte Ubuntu-Version verwenden.
- Einführung in die Linux-Befehlszeile: Als Einführung oder Auffrischung Ihres Wissens über die Befehlszeile können Sie sich diesen Leitfaden zur Linux-Befehlszeile ansehen.
- Grundkenntnisse der Python-Programmierung.
- Auf Ihrem Ubuntu-System muss Python 3.7 oder höher installiert sein. Eine Anleitung zum Ausführen von Python-Skripten unter Ubuntu finden Sie in unserem entsprechenden Tutorial.
Einrichten Ihrer Flask-Umgebung
Ubuntu 24.04 wird standardmäßig mit Python 3 ausgeliefert. Öffnen Sie ein Terminal und führen Sie den folgenden Befehl aus, um Ihre Python-3-Installation zu überprüfen:
root@ubuntu:~# python3 --version
Python 3.12.3Wenn Python 3 bereits auf Ihrem Rechner installiert ist, gibt der obige Befehl die aktuelle Version Ihrer Python-3-Installation zurück. Falls Python 3 nicht installiert ist, können Sie den folgenden Befehl ausführen, um die installierte Version zu ermitteln:
root@ubuntu:~# sudo apt install python3
Als Nächstes müssen Sie den Paketmanager pip auf Ihrem System installieren:
root@ubuntu:~# sudo apt install python3-pip
Nach der Installation von pip installieren wir nun Flask.
Sie installieren Flask über pip. Es wird empfohlen, dies in einer virtuellen Umgebung durchzuführen, um Konflikte mit anderen Paketen auf Ihrem System zu vermeiden.
root@ubuntu:~# python3 -m venv myprojectenv root@ubuntu:~# source myprojectenv/bin/activate
root@ubuntu:~# pip install Flask
Erstellen Sie eine Flask-Anwendung
Der nächste Schritt ist das Schreiben des Python-Codes für die Flask-Anwendung. Um ein neues Skript zu erstellen, wechseln Sie in das gewünschte Verzeichnis:
root@ubuntu:~# cd ~/path-to-your-script-directory
Sobald Sie sich im entsprechenden Verzeichnis befinden, erstellen Sie eine neue Python-Datei namens app.py und importieren Sie Flask. Initialisieren Sie anschließend eine Flask-Anwendung und legen Sie einen Stammverzeichnispfad fest.
root@ubuntu:~# nano app.py
Dadurch öffnet sich ein leerer Texteditor. Schreiben Sie hier Ihre Logik oder kopieren Sie den unten stehenden Code:
from flask import Flask, jsonify, request
app = Flask(__name__)
# Simulate a slow endpoint
@app.route('/slow')
def slow():
import time
time.sleep(2) # to simulate a slow response
return jsonify(message="This request was slow!")
# Simulate an intensive database operation
@app.route('/db')
def db_operation():
# This is a dummy function to simulate a database query
result = {"name": "User", "email": "[email protected]"}
return jsonify(result)
# Simulate a static file being served
@app.route('/')
def index():
return "<h1>Welcome to the Sample Flask App</h1>"
if __name__ == '__main__':
app.run(debug=True)Jetzt starten wir die Flask-Anwendung:
root@ubuntu:~# flask run
Sie können die Endpunkte mit den folgenden curl-Befehlen testen:
Testen Sie den /-Endpunkt (gibt statischen Inhalt zurück):
root@ubuntu:~# curl http://127.0.0.1:5000/[secondary_lebel Output]
<h1>Welcome to the Sample Flask App</h1>%Testen Sie den Endpunkt/langsam (simuliert eine langsame Reaktion):
root@ubuntu:~# time curl http://127.0.0.1:5000/db
Um diesen langsamen Endpunkt zu überprüfen, verwenden wir den Befehl `time` in Linux. Der Befehl `time` dient dazu, die Ausführungszeit eines bestimmten Befehls oder Programms zu messen. Er liefert drei Hauptinformationen:
- Echtzeit: Die tatsächlich verstrichene Zeit vom Beginn bis zum Ende des Befehls.
- Benutzerzeit: Die im Benutzermodus verbrachte CPU-Zeit.
- Systemzeit: Die im Kernelmodus verbrachte CPU-Zeit.
Dies hilft uns, die tatsächliche Laufzeit unseres langsamsten Endpunkts zu messen. Die Ausgabe könnte etwa so aussehen:
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 totalDie Beantwortung dieser Anfrage dauert etwa 2 Sekunden, da der Aufruf time.sleep(2) eine langsame Antwort simuliert.
Testen wir den /db-Endpunkt (simuliert Datenbankoperationen):
root@ubuntu:~# curl http://127.0.0.1:5000/db
Output
{"email":"[email protected]","name":"User"}Durch das Testen dieser Endpunkte mit curl können Sie überprüfen, ob Ihre Flask-Anwendung korrekt ausgeführt wird und ob die Antworten den Erwartungen entsprechen.
Im nächsten Abschnitt lernen Sie, wie Sie die Anwendungsleistung mithilfe verschiedener Techniken optimieren können.
Verwenden Sie einen produktionsreifen WSGI-Server.
Der in Flask integrierte Entwicklungsserver ist nicht für Produktionsumgebungen ausgelegt. Um gleichzeitige Anfragen effizient zu verarbeiten, sollten Sie auf einen produktionsreifen WSGI-Server wie Gunicorn umsteigen.
Installieren und starten Sie Gunicorn
Installieren wir Gunicorn.
root@ubuntu:~# pip install gunicornFühren Sie die Flask-Anwendung mit Gunicorn und 4 Worker-Prozessen aus:
root@ubuntu:~# gunicorn -w 4 -b 0.0.0.0:8000 app:app
Output
% /Library/Python/3.9/bin/gunicorn -w 4 -b 0.0.0.0:8000 app:app
[2024-09-13 18:37:24 +0530] [99925] [INFO] Starting gunicorn 23.0.0
[2024-09-13 18:37:24 +0530] [99925] [INFO] Listening at: http://0.0.0.0:8000 (99925)
[2024-09-13 18:37:24 +0530] [99925] [INFO] Using worker: sync
[2024-09-13 18:37:24 +0530] [99926] [INFO] Booting worker with pid: 99926
[2024-09-13 18:37:25 +0530] [99927] [INFO] Booting worker with pid: 99927
[2024-09-13 18:37:25 +0530] [99928] [INFO] Booting worker with pid: 99928
[2024-09-13 18:37:25 +0530] [99929] [INFO] Booting worker with pid: 99929
[2024-09-13 18:37:37 +0530] [99925] [INFO] Handling signal: winch
^C[2024-09-13 18:38:51 +0530] [99925] [INFO] Handling signal: int
[2024-09-13 18:38:51 +0530] [99927] [INFO] Worker exiting (pid: 99927)
[2024-09-13 18:38:51 +0530] [99926] [INFO] Worker exiting (pid: 99926)
[2024-09-13 18:38:51 +0530] [99928] [INFO] Worker exiting (pid: 99928)
[2024-09-13 18:38:51 +0530] [99929] [INFO] Worker exiting (pid: 99929)
[2024-09-13 18:38:51 +0530] [99925] [INFO] Shutting down: MasterHier die Vorteile der Nutzung von Gunicorn:
- Gleichzeitige Bearbeitung von Anfragen: Gunicorn ermöglicht die gleichzeitige Bearbeitung mehrerer Anfragen mithilfe mehrerer Worker-Prozesse.
- Lastverteilung: Verteilt eingehende Anfragen gleichmäßig auf die Worker-Prozesse und gewährleistet so eine optimale Nutzung der Serverressourcen.
- Asynchrone Worker: Mit asynchronen Workern wie gevent können langlaufende Aufgaben effizient ausgeführt werden, ohne andere Anfragen zu blockieren.
- Skalierbarkeit: Gunicorn kann horizontal skaliert werden, indem die Anzahl der Worker-Prozesse erhöht wird, um mehr gleichzeitige Anfragen zu bearbeiten.
- Fehlertoleranz: Ersetzt automatisch nicht reagierende oder defekte Mitarbeiter und gewährleistet so eine hohe Verfügbarkeit.
- Produktionsbereit: Im Gegensatz zum Flask-Entwicklungsserver ist Gunicorn für Produktionsumgebungen optimiert und bietet bessere Sicherheits-, Stabilitäts- und Leistungsmerkmale.
Durch den Wechsel zu Gunicorn für den Produktiveinsatz können Sie den Durchsatz und die Reaktionsfähigkeit Ihrer Flask-Anwendung deutlich verbessern und sie so optimal auf die Bewältigung des realen Datenverkehrs vorbereiten.
Aktivieren Sie Caching, um die Last zu reduzieren.
Caching ist eine der besten Möglichkeiten, die Flask-Performance durch Reduzierung des Overheads zu verbessern. Hier fügen Sie Flask-Caching hinzu, um das Ergebnis des langsamen Pfades zwischenzuspeichern.
Flask-Caching mit Redis installieren und konfigurieren
Installieren Sie die erforderlichen Pakete:
Aktualisiere app.py, um den Cache zu slow/path hinzuzufügen.
Öffnen Sie den Editor und aktualisieren Sie die Datei app.py mit folgendem Befehl:
root@ubuntu:~# nano app.py
from flask_caching import Cache
app = Flask(__name__)
# Configure Flask-Caching with Redis
app.config['CACHE_TYPE'] = 'redis'
app.config['CACHE_REDIS_HOST'] = 'localhost'
app.config['CACHE_REDIS_PORT'] = 6379
cache = Cache(app)
@app.route('/slow')
@cache.cached(timeout=60)
def slow():
import time
time.sleep(2) # Simulate a slow response
return jsonify(message="This request was slow!")Nach der ersten Anfrage an /slow werden nachfolgende Anfragen innerhalb von 60 Sekunden aus dem Cache bedient, wodurch die Funktion time.sleep() umgangen wird. Dies reduziert die Serverlast und erhöht die Antwortgeschwindigkeit.
Hinweis: In diesem Tutorial verwenden wir localhost als Redis-Host. In einer Produktionsumgebung empfiehlt sich jedoch die Verwendung eines verwalteten Redis-Dienstes wie DigitalOcean Managed Redis. Dieser bietet bessere Skalierbarkeit, Zuverlässigkeit und Sicherheit für Ihre Speicheranforderungen. Weitere Informationen zur Integration von DigitalOcean Managed Redis in eine Produktionsanwendung finden Sie im Tutorial „Speicherung mit DigitalOcean Redis auf der Anwendungsplattform“.
Um zu überprüfen, ob Daten zwischengespeichert werden, führen wir die folgenden Befehle für den Endpunkt /slow aus.
Dies ist die erste Anfrage an den Endpunkt /slow. Nach Abschluss dieser Anfrage wird das Ergebnis der Route /slow zwischengespeichert.
root@ubuntu:~# time curl http://127.0.0.1:5000/slow
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 totalDies ist eine Folgeanfrage an den Endpunkt /slow innerhalb von 60 Sekunden:
root@ubuntu:~# time curl http://127.0.0.1:5000/slow
Output
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.00s system 0% cpu 0.015 totalDatenbankabfragen optimieren
Datenbankabfragen können häufig zu einem Leistungsengpass werden. In diesem Abschnitt simulieren Sie die Optimierung von Datenbankabfragen mithilfe von SQLAlchemy und Join-Pooling.
Simulieren einer Datenbankabfrage mit Join-Aggregation
Als Erstes installieren wir SQLAlchemy.
root@ubuntu:~# pip install Flask-SQLAlchemy
Aktualisieren Sie app.py, um die Verbindungsintegration zu konfigurieren.
rom flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
# Simulate an intensive database operation
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_POOL_SIZE'] = 5 # Connection pool size
db = SQLAlchemy(app)
@app.route('/db1')
def db_operation_pooling():
# Simulate a database query
result = db.session.execute(text('SELECT 1')).fetchall()
return jsonify(result=str(result))Wenn wir nun die curl-Anfrage an den Pfad db1 ausführen, sollten wir folgende Ausgabe sehen:
root@ubuntu:~# curl http://127.0.0.1:5000/db1
output
{"result":"[(1,)]"}Die Performance Ihrer Flask-Anwendung lässt sich durch den Einsatz von Connection Pooling in einer Produktionsumgebung deutlich optimieren. Connection Pooling ermöglicht es der Anwendung, bestehende Datenbankverbindungen wiederzuverwenden, anstatt für jede Anfrage neue Verbindungen herzustellen. Dies reduziert den Aufwand für die Erstellung neuer Verbindungen, was zu schnelleren Antwortzeiten und verbesserter Skalierbarkeit führt.
Die zuvor festgelegte Konfiguration SQLALCHEMY_POOL_SIZE begrenzt die Anzahl der Verbindungen im Pool. In einer Produktionsumgebung sollten Sie diesen Wert an Ihre spezifischen Anforderungen und die Leistungsfähigkeit Ihres Servers anpassen. Zusätzlich können Sie weitere Integrationsoptionen wie SQLALCHEMY_MAX_OVERFLOW in Betracht ziehen, um zusätzliche Verbindungen zu ermöglichen, falls der Pool voll ist, und SQLALCHEMY_POOL_TIMEOUT, um die Wartezeit einer Anfrage auf eine Verbindung festzulegen.
Bedenken Sie, dass wir in unserem Beispiel zwar aus Gründen der Einfachheit SQLite verwenden, in realen Anwendungsszenarien aber wahrscheinlich eine robustere Datenbank wie PostgreSQL oder MySQL zum Einsatz kommt. Diese Datenbanken verfügen über eigene Verbindungspooling-Mechanismen, die in Verbindung mit der SQLAlchemy-Integration für eine bessere Performance genutzt werden können.
Durch sorgfältige Konfiguration und den Einsatz von Connection Pooling können Sie sicherstellen, dass Ihre Flask-Anwendung Datenbankoperationen auch unter hoher Last effizient durchführt und somit ihre Gesamtleistung deutlich verbessert.
Gzip-Komprimierung aktivieren
Durch die Komprimierung Ihrer Antworten kann die Menge der zwischen Server und Clients übertragenen Daten drastisch reduziert und die Leistung verbessert werden.
Flask-Compress installieren und konfigurieren
Installieren wir das Flask-compress-Paket.
root@ubuntu:~# pip install Flask-Compress
Als Nächstes aktualisieren wir app.py, um die Komprimierung zu aktivieren.
from flask_compress import Compress
# This below command enables Gzip compression for the Flask app
# It compresses responses before sending them to clients,
# reducing data transfer and improving performance
Compress(app)
@app.route('/compress')
def Compress():
return "<h1>Welcome to the optimized Flask app !</h1>"Dadurch werden Antworten, die größer als 500 Byte sind, automatisch komprimiert, wodurch die Übertragungszeit großer Antworten verkürzt wird.
In einer Produktionsumgebung kann die Gzip-Komprimierung die zwischen Server und Clients übertragene Datenmenge erheblich reduzieren, insbesondere bei textbasierten Inhalten wie HTML, CSS und JavaScript.
Diese Reduzierung des Datentransfers führt zu kürzeren Ladezeiten, einer verbesserten Benutzererfahrung und geringeren Bandbreitenkosten. Darüber hinaus unterstützen viele moderne Webbrowser die Gzip-Komprimierung automatisch, wodurch sie zu einer vollständig kompatiblen Optimierungstechnik wird. Durch die Aktivierung der Gzip-Komprimierung können Sie die Leistung und Skalierbarkeit Ihrer Flask-Anwendung effektiv verbessern, ohne clientseitige Änderungen vornehmen zu müssen.
Hochladen rechenintensiver Aufgaben auf Celery
Für ressourcenintensive Vorgänge wie das Versenden von E-Mails oder die Verarbeitung großer Datensätze empfiehlt es sich, diese mithilfe von Celery in Hintergrundprozesse auszulagern. Dadurch wird verhindert, dass langlaufende Aufgaben eingehende Anfragen blockieren.
Celery ist ein leistungsstarkes, verteiltes Aufgabenwarteschlangensystem, mit dem Sie zeitaufwändige Aufgaben asynchron ausführen können. Durch die Auslagerung rechenintensiver Operationen an Celery verbessern Sie die Reaktionsfähigkeit und Skalierbarkeit Ihrer Flask-Anwendung deutlich. Celery delegiert Aufgaben an Worker-Prozesse, die auf separaten Rechnern laufen können, was eine bessere Ressourcennutzung und parallele Verarbeitung ermöglicht.
Zu den wichtigsten Vorteilen der Verwendung von Sellerie gehören:
- Verbesserung der Reaktionszeit auf Benutzeranfragen
- Bessere Skalierbarkeit und Ressourcenverwaltung
- Fähigkeit, komplexe und zeitaufwändige Aufgaben auszuführen, ohne das Hauptprogramm zu blockieren
- Integrierte Unterstützung für die Aufgabenplanung und das Wiederholen fehlgeschlagener Aufgaben
- Einfache Integration mit verschiedenen Message Brokern wie RabbitMQ oder Redis
Mit Celery können Sie sicherstellen, dass Ihre Flask-Anwendung auch bei rechenintensiven oder E/A-lastigen Aufgaben reaktionsfähig bleibt.
Celery für Hintergrundaufgaben konfigurieren
Installieren wir Celery.
root@ubuntu:~# pip install Celery
Als Nächstes aktualisieren wir app.py, um Celery für asynchrone Aufgaben zu konfigurieren:
from celery import Celery
celery = Celery(app.name, broker='redis://localhost:6379/0')
@celery.task
def long_task():
import time
time.sleep(10) # Simulate a long task
return "Task Complete"
@app.route('/start-task')
def start_task():
long_task.delay()
return 'Task started'Starten Sie in einem separaten Terminal den Celery-Worker:
root@ubuntu:~# celery -A app.celery worker --loglevel=info
Output
------------- celery@your-computer-name v5.2.7 (dawn-chorus)
--- ***** -----
-- ******* ---- Linux-x.x.x-x-generic-x86_64-with-glibc2.xx 2023-xx-xx
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: app:0x7f8b8c0b3cd0
- ** ---------- .> transport: redis://localhost:6379/0
- ** ---------- .> results: disabled://
- *** --- * --- .> concurrency: 8 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. app.long_task
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Connected to redis://localhost:6379/0
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: searching for neighbors
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: all alone
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] celery@your-computer-name ready.Führen Sie nun einen curl-Befehl aus, der den Pfad /start-task ansteuert. Die Ausgabe sieht wie folgt aus:
root@ubuntu:~# curl http://127.0.0.1:5000/start-task
Output
Task startedDies gibt fast sofort die Meldung «Aufgabe gestartet» zurück, selbst wenn der Hintergrundprozess noch läuft.
Die Funktion start_task() bewirkt zwei Dinge:
- Ruft `long_task.delay()` auf, wodurch die Celery-Aufgabe asynchron gestartet wird. Das bedeutet, dass die Aufgabe in die Warteschlange gestellt wird, um im Hintergrund ausgeführt zu werden, die Funktion aber nicht auf deren Abschluss wartet.
- Es gibt sofort die Zeichenkette «Aufgabe gestartet» zurück.
Wichtig ist, dass die eigentliche, langlaufende Aufgabe (simuliert durch die 10-sekündige Wartezeit) asynchron von Celery ausgeführt wird. Die Flask-Route wartet nicht auf den Abschluss dieser Aufgabe, bevor sie auf die Anfrage antwortet.
Wenn Sie also diesen Endpunkt beschädigen, erhalten Sie sofort die Antwort «Job gestartet», während der eigentliche Job noch 10 Sekunden lang im Hintergrund weiterläuft.
Nach 10 Sekunden, wenn der Hintergrundprozess abgeschlossen ist, sollte folgende Meldung angezeigt werden:
Die Ausgabe wird in etwa so aussehen:
[2024-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Task app.long_task[task-id] received
[2024-xx-xx xx:xx:xx,xxx: INFO/ForkPoolWorker-1] Task app.long_task[task-id] succeeded in 10.xxxs: 'Task Complete'Dieses Beispiel zeigt, wie Celery die Performance einer Flask-Anwendung verbessert, indem es langlaufende Aufgaben asynchron ausführt und so die Reaktionsfähigkeit der Hauptanwendung aufrechterhält. Die langlaufende Aufgabe läuft im Hintergrund, wodurch die Flask-Anwendung für die Bearbeitung anderer Anfragen frei wird.
In einer Produktionsumgebung umfasst der Betrieb von Celery Folgendes:
- Verwendung eines robusten Message Brokers wie RabbitMQ
- Verwendung eines proprietären Ergebnis-Backends (z. B. PostgreSQL)
- Mitarbeiter mithilfe von Prozessleitsystemen steuern (z. B. durch Vorgesetzte).
- Implementieren Sie Überwachungstools (z. B. Blumen). Verbessern Sie die Fehlerbehandlung und Protokollierung.
- Verbesserte Fehlerbehandlung und Protokollierung
- Nutzen Sie die Aufgabenpriorisierung.
- Skalierung mit mehreren Mitarbeitern auf verschiedenen Maschinen
- Sicherstellung angemessener Sicherheitsmaßnahmen
Ergebnis
In diesem Tutorial haben Sie gelernt, wie Sie Ihre Flask-Anwendung durch die Implementierung verschiedener Leistungsoptimierungstechniken optimieren können. Indem Sie diese Schritte befolgen, verbessern Sie die Leistung, Skalierbarkeit und Reaktionsfähigkeit Ihrer Flask-Anwendung und stellen sicher, dass sie auch unter hoher Last effizient läuft.









