init at work

This commit is contained in:
Mathias Schneider
2026-03-27 12:58:01 +01:00
parent 352c352056
commit 8d40597732
60 changed files with 16498 additions and 0 deletions
+469
View File
@@ -0,0 +1,469 @@
## 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 PythonApplikation (z.B. [[FastAPI]], Starlette, Django mit ASGI)
Uvicorn basiert intern auf sehr schnellen CBibliotheken:
- **uvloop** (schneller Event Loop, Ersatz für `asyncio`Loop)
- **httptools** (schnelles HTTPParsing)
Du verwendest uvicorn typischerweise, um eine ASGIApp „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 WebSocketSupport
- 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/ResponseLogik
- **uvicorn**:
- kümmert sich um das Annehmen von Verbindungen, HTTPParsing, EventLoopHandling.
- ruft deine Applikation nur gemäß dem ASGIProtokoll 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 syncApps 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 WorkerSpawner
- uvicorn = eigentlicher ASGIServer pro Worker
### 3.2 uvicorn vs. Hypercorn / Daphne
- **hypercorn**:
- anderer ASGIServer (unterstützt z.B. HTTP/2, verschiedene Event Loops)
- **daphne**:
- ASGIServer aus dem DjangoChannels‑Ökosystem
Alle drei (uvicorn, hypercorn, daphne) machen im Kern das Gleiche:
**ASGIApps 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 WebBackends 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 WebSocketEndpoint nicht möglich.
### 4.3 Produktionstauglicher Server gegenüber Entwicklungsservern
- Stabilität bei hoher Last
- Steuerung von:
- Anzahl WorkerProzesse
- 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 ASGIApp 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 CLIOptionen:
- `--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 CPUAufgaben
- synchrones Warten auf externe APIs (z.B. `requests.get(...)`)
- schwere DatenbankQueries, 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 syncDBClient)
- CPUlastig: in ThreadPool oder ProcessPool 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. DBConnections 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 LinuxServern oder in DockerContainern.
---
## 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**
- BlockingCode prüfen (CPU, I/O)
- Datenbankzugriff sauber konfigurieren (Pools, asyncClient)
- Logging und ErrorHandling 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 **ASGIWebserver** für Python, optimiert für **asynchrone** WebApps.
- Er ist **kein WebFramework**, sondern die Laufzeitumgebung für Frameworks wie **[[FastAPI]]**, **Starlette** oder moderne **Django**Konfigurationen.
- Es löst die Probleme klassischer WSGIServer in Bezug auf **Async**, **WebSockets** und **Performance**.
- Typische Herausforderungen liegen im Bereich:
- korrektes AsyncDesign
- Umgang mit mehreren Workern und gemeinsamem Zustand
- saubere Integration von Datenbanken, Logging, Deployment
- Für dich als PythonEntwickler ist uvicorn im Alltag vor allem:
**das Kommando, mit dem du deine moderne WebAPI startest.**