## 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“: ```bash 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.UvicornWorker` auch ASGI-App starten. Beispiel: [[FastAPI]]‑App mit gunicorn + uvicorn worker: ```bash 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 def` Endpoints schreiben - WebSockets nutzen - viele gleichzeitige Requests mit einem Event Loop bedienen uvicorn ermöglicht dir, diese **asynchronen** Apps performant auszuliefern. Praxisnahes Beispiel ([[FastAPI]]): ```python # 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: ```bash 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: ```python # 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: ```bash 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 ```bash 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: ```python # 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: ```bash uvicorn app:app --reload ``` Erklärung: - `app:app` = Modul `app.py`, Variable `app` - `--reload` = automatischer Neustart bei Codeänderung (nur dev) ### 5.3 Beispiel mit [[FastAPI]] ```python # main.py from fastapi import FastAPI app = FastAPI() @app.get("/") async def root(): return {"message": "Hello from uvicorn + FastAPI"} ``` Start: ```bash 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.0` um von außen erreichbar zu sein - `--port`: Port, z.B. `8000` - `--workers`: Anzahl der Prozesse (für Produktion) ### 5.4 Starten aus Python heraus ```python # run.py import uvicorn if __name__ == "__main__": uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=True, ) ``` Start: ```bash python run.py ``` --- ## 6. Typische Konfigurationen und Szenarien ### 6.1 Entwicklung - ein Worker - `--reload` aktiviert - Logging auf `debug` ```bash 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 `info` oder `warning` ```bash 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: ```nginx 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: ```python @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. `httpx` statt `requests`, `asyncpg` statt sync‑DB‑Client) - CPU‑lastig: in Thread‑Pool oder Process‑Pool auslagern (`run_in_threadpool` etc.) ### 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): ```python 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): ```python 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): ```bash 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) - `--reload` nutzt 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 1. **Framework wählen** - [[FastAPI]] oder Starlette, wenn du intensiv async nutzen willst. 1. **App schreiben** - `app = FastAPI()`, Endpoints definieren. 3. **In Entwicklung starten** ```bash uvicorn main:app --reload ``` 4. **Vor Produktion** - Blocking‑Code prüfen (CPU, I/O) - Datenbankzugriff sauber konfigurieren (Pools, async‑Client) - Logging und Error‑Handling aufräumen 5. **In Produktion starten** (einfach) ```bash uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 ``` 6. **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.**