init at work
This commit is contained in:
Executable
+469
@@ -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 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.**
|
||||
|
||||
Reference in New Issue
Block a user