12 KiB
Executable File
1. Grundsätzliche Definition: Was ist uvicorn?
uvicorn ist ein extrem performanter, asynchroner Web-Server für Python, der das ASGI‑Protokoll unterstützt.
- ASGI = Asynchronous Server Gateway Interface
- uvicorn ist also das Bindeglied zwischen:
- dem Web (HTTP, WebSockets)
- und deiner Python‑Applikation (z.B. FastAPI, Starlette, Django mit ASGI)
Uvicorn basiert intern auf sehr schnellen C‑Bibliotheken:
- uvloop (schneller Event Loop, Ersatz für
asyncio‑Loop) - httptools (schnelles HTTP‑Parsing)
Du verwendest uvicorn typischerweise, um eine ASGI‑App „zu starten“:
uvicorn main:app --reload
2. Wichtige Begriffe: ASGI, WSGI und Web-Frameworks
2.1 ASGI vs. WSGI
-
WSGI (älterer Standard, z.B. für Django (klassisch), Flask):
- synchron
- kein natives WebSocket‑Support
- typische Server:
gunicorn,uWSGI,mod_wsgi
-
ASGI (moderner Standard):
- unterstützt async/await
- kann HTTP und WebSockets und Background Tasks
- typische Server:
uvicorn,hypercorn,daphne
uvicorn ist also ein ASGI-Server, nicht WSGI.
2.2 uvicorn vs. Web-Frameworks (FastAPI, Starlette, Django, Flask)
-
Framework (FastAPI, Starlette, Django, Flask):
- definiert, wie du Routen, Views, Models, etc. schreibst.
- kümmert sich um Request/Response‑Logik
-
uvicorn:
- kümmert sich um das Annehmen von Verbindungen, HTTP‑Parsing, Event‑Loop‑Handling.
- ruft deine Applikation nur gemäß dem ASGI‑Protokoll auf.
Bildlich:
Browser → (HTTP) → uvicorn → (ASGI) → deine App (z.B. FastAPI)
3. Abgrenzung zu ähnlichen oder verwandten Begriffen
3.1 uvicorn vs. Gunicorn
- gunicorn:
- klassischer WSGI-Server (für sync‑Apps wie Flask oder Django ohne ASGI).
- kann aber mithilfe von Workern wie
uvicorn.workers.UvicornWorkerauch ASGI-App starten.
Beispiel: FastAPI‑App mit gunicorn + uvicorn worker:
gunicorn -k uvicorn.workers.UvicornWorker main:app -b 0.0.0.0:8000
Hier ist:
- gunicorn = Prozessmanager und Worker‑Spawner
- uvicorn = eigentlicher ASGI‑Server pro Worker
3.2 uvicorn vs. Hypercorn / Daphne
- hypercorn:
- anderer ASGI‑Server (unterstützt z.B. HTTP/2, verschiedene Event Loops)
- daphne:
- ASGI‑Server aus dem Django‑Channels‑Ökosystem
Alle drei (uvicorn, hypercorn, daphne) machen im Kern das Gleiche:
ASGI‑Apps ausführen, unterscheiden sich aber in Features, Performance und Konfigurationsmöglichkeiten.
3.3 uvicorn vs. „eingebauter Development-Server“
Viele Frameworks haben eingebaute Dev-Server, z.B.:
- Flask:
app.run(debug=True) - Django:
python manage.py runserver
Diese sind:
- für Entwicklung gedacht
- nicht für Produktion (Performance, Stabilität, Security)
uvicorn ist ein richtiger Webserver, der für Produktion geeignet ist (oft zusammen mit einem Reverse Proxy wie Nginx).
4. Welche Probleme löst uvicorn?
4.1 Asynchrone Web‑Backends performant betreiben
Mit ASGI kannst du:
async defEndpoints schreiben- WebSockets nutzen
- viele gleichzeitige Requests mit einem Event Loop bedienen
uvicorn ermöglicht dir, diese asynchronen Apps performant auszuliefern.
Praxisnahes Beispiel (FastAPI):
# main.py
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
await asyncio.sleep(1) # simuliert eine I/O-Operation
return {"item_id": item_id}
Starten mit uvicorn:
uvicorn main:app --reload
uvicorn kümmert sich darum, dass mehrere Requests gleichzeitig abgearbeitet werden können, während asyncio.sleep nicht blockiert.
4.2 WebSockets und Long-Lived Connections
ASGI (und damit uvicorn) unterstützt WebSockets nativ, was mit WSGI nicht geht.
Beispiel mit Starlette:
# main.py
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.websockets import WebSocket
from starlette.routing import Route, WebSocketRoute
async def homepage(request):
return JSONResponse({"hello": "world"})
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
await websocket.send_text("Willkommen!")
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Du hast gesendet: {data}")
routes = [
Route("/", endpoint=homepage),
WebSocketRoute("/ws", endpoint=websocket_endpoint),
]
app = Starlette(routes=routes)
Start:
uvicorn main:app
Mit WSGI wäre so ein WebSocket‑Endpoint nicht möglich.
4.3 Produktionstauglicher Server gegenüber Entwicklungsservern
- Stabilität bei hoher Last
- Steuerung von:
- Anzahl Worker‑Prozesse
- Timeouts
- Logging
- Start via CLI, systemd, Docker, Kubernetes etc.
5. Grundlegende Verwendung von uvicorn
5.1 Installation
pip install uvicorn
# optional: schnellere Variante mit C-Extensions
pip install "uvicorn[standard]"
[standard] installiert u.a. uvloop und httptools.
5.2 Minimalbeispiel: Plain-ASGI-App
Du kannst eine ASGI‑App auch ohne Framework schreiben:
# app.py
async def app(scope, receive, send):
assert scope["type"] == "http"
# Request body lesen (vereinfachter Fall)
await receive()
body = b"Hello, world"
headers = [(b"content-type", b"text/plain")]
await send({
"type": "http.response.start",
"status": 200,
"headers": headers,
})
await send({
"type": "http.response.body",
"body": body,
})
Starten:
uvicorn app:app --reload
Erklärung:
app:app= Modulapp.py, Variableapp--reload= automatischer Neustart bei Codeänderung (nur dev)
5.3 Beispiel mit FastAPI
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello from uvicorn + FastAPI"}
Start:
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Wichtige CLI‑Optionen:
--reload: Auto-Reload bei Codeänderungen (Dev)--host: z.B.0.0.0.0um von außen erreichbar zu sein--port: Port, z.B.8000--workers: Anzahl der Prozesse (für Produktion)
5.4 Starten aus Python heraus
# run.py
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True,
)
Start:
python run.py
6. Typische Konfigurationen und Szenarien
6.1 Entwicklung
- ein Worker
--reloadaktiviert- Logging auf
debug
uvicorn main:app --reload --host 0.0.0.0 --port 8000 --log-level debug
6.2 Produktion (einfach)
- mehrere Worker-Prozesse
- kein
--reload - Logging eher
infooderwarning
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 --log-level info
Richtwert für Worker:
Anzahl CPU-Kerne * 2 (abhängig von App und Last; immer testen).
6.3 Produktion hinter einem Reverse Proxy (z.B. Nginx)
Typischer Aufbau:
Internet → Nginx (TLS, gzip, etc.) → uvicorn → FastAPI/Starlette/Django
- Nginx übernimmt TLS/SSL, Load Balancing, Static Files
- uvicorn macht die Application-Logik
Nginx-Konfig (stark vereinfacht) könnte so aussehen:
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
uvicorn wird weiterhin wie oben gestartet.
7. Herausforderungen und typische Stolpersteine
7.1 Async/Synchron-Mix und Blockierungen
Problem:
Du verwendest uvicorn (ASGI, async), aber in deinen Endpoints gibt es blockierende Operationen:
- große CPU‑Aufgaben
- synchrones Warten auf externe APIs (z.B.
requests.get(...)) - schwere Datenbank‑Queries, die nicht async sind
Beispiel:
@app.get("/slow")
async def slow():
import time
time.sleep(5) # BLOCKIERT den Event-Loop
return {"status": "ok"}
Folge:
- Ein Request blockiert den Event Loop → alle anderen Requests warten mit.
Lösungen:
- I/O: async Libraries benutzen (z.B.
httpxstattrequests,asyncpgstatt sync‑DB‑Client) - CPU‑lastig: in Thread‑Pool oder Process‑Pool auslagern (
run_in_threadpooletc.)
7.2 Gemeinsamer Zustand über Worker-Prozesse
Wenn du --workers > 1 nutzt, hast du mehrere Prozesse.
Globaler Zustand in Python wird nicht zwischen Prozessen geteilt.
Beispiel (Problem):
counter = 0
@app.get("/count")
def count():
global counter
counter += 1
return {"counter": counter}
Mit mehreren Workern:
- jeder Worker hat seinen eigenen
counter - Ergebnisse sind inkonsistent
Lösung:
- geteilten Zustand über externe Systeme (Redis, Datenbank, etc.)
- oder nur einen Worker nutzen, wenn globaler In-Memory-State unvermeidbar ist (aber meist unsauber).
7.3 Datenbankverbindungen und Lebenszyklus
uvicorn unterstützt ASGI‑lifespan‑Events (startup/shutdown).
Frameworks wie FastAPI/Starlette nutzen das, um z.B. DB‑Connections zu öffnen/schließen.
Stolpersteine:
- Verbindungspools pro Worker korrekt initialisieren
- bei Shutdown sauber schließen
- nicht „pro Request“ neue Connections aufmachen
Beispiel mit FastAPI (vereinfacht):
from fastapi import FastAPI
app = FastAPI()
db = None
@app.on_event("startup")
async def startup():
global db
db = await some_async_db_connect()
@app.on_event("shutdown")
async def shutdown():
await db.close()
7.4 Logging und Error-Handling
uvicorn hat eigenes Logging; dein Framework ebenso.
Typische Themen:
- Log-Format in Produktion standardisieren
- Fehler-Logs im Zusammenspiel mit Reverse Proxy
- Ausführliche Logs in Dev, weniger in Prod
Beispiel (JSON-Logging in Produktion, nur angedeutet):
uvicorn main:app \
--host 0.0.0.0 \
--port 8000 \
--log-config logging_config.yaml
In logging_config.yaml kannst du detailliert das Logging steuern.
7.5 Plattformunterschiede (Windows vs. Linux)
--reloadnutzt File-Watcher und Signale → unter Linux sehr stabil; unter Windows kann es ein paar Besonderheiten geben.- In Produktion läuft uvicorn meist auf Linux‑Servern oder in Docker‑Containern.
8. Kurze Checkliste für den praktischen Einstieg
- Framework wählen
- FastAPI oder Starlette, wenn du intensiv async nutzen willst.
- App schreiben
app = FastAPI(), Endpoints definieren.
- In Entwicklung starten
uvicorn main:app --reload - Vor Produktion
- Blocking‑Code prüfen (CPU, I/O)
- Datenbankzugriff sauber konfigurieren (Pools, async‑Client)
- Logging und Error‑Handling aufräumen
- In Produktion starten (einfach)
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 - Optional:
- vor uvicorn einen Nginx oder Traefik setzen (TLS, Load Balancing)
9. Zusammenfassung
- uvicorn ist ein ASGI‑Webserver für Python, optimiert für asynchrone Web‑Apps.
- Er ist kein Web‑Framework, sondern die Laufzeitumgebung für Frameworks wie FastAPI, Starlette oder moderne Django‑Konfigurationen.
- Es löst die Probleme klassischer WSGI‑Server in Bezug auf Async, WebSockets und Performance.
- Typische Herausforderungen liegen im Bereich:
- korrektes Async‑Design
- Umgang mit mehreren Workern und gemeinsamem Zustand
- saubere Integration von Datenbanken, Logging, Deployment
- Für dich als Python‑Entwickler ist uvicorn im Alltag vor allem:
das Kommando, mit dem du deine moderne Web‑API startest.