Mécanismes de concurrence dans Uvicorn
Uvicorn exploite une architecture asynchrone et événementielle pour gérer efficacement les connexions concurrentes. Le contrôle précis du nombre maximal de connexions est assuré par le paramètre limit_concurrency, défini dans les modules de protocole HTTP. Ce serveur ASGI offre plusieurs options de configuration pour adapter le parallélisme aux besoins.
- Nombre de processus travailleurs : configurable via
--workers - Limite de connexions simultanées : ajustable avec
--limit-concurrency - Réutilisation des connexions : support de HTTP/1.1 Keep-Alive
Environnement de test préliminaire
Avant d'effectuer des tests de performance, il est essentiel de préparer un environnement reproductible. Des benchmarks intégrés sont disponibles dans le répertoire tests/benchmarks/ pour évaluer les performances HTTP et WebSocket.
Outils de test recommandés
Pour simuler une charge élevée, plusieurs outils s'avèrent adaptés :
- wrk – Outil de benchmark HTTP haute performance
- locust – Framework de test de charge distribué
- ab – Utilitaire de benchmark pour serveurs Apache
- siege – Outil de test de stress pour protocoles HTTP/HTTPS
Application de test minimale
Créez une application ASGI simple pour émuler des scénarios réels :
# service_demo.py
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import JSONResponse
import asyncio
async def index(request):
return JSONResponse({"status": "active"})
async def delayed_route(request):
await asyncio.sleep(0.08) # Simulation d'un traitement I/O
return JSONResponse({"result": "computed"})
app = Starlette(routes=[
Route("/", index),
Route("/compute", delayed_route),
])
Lancement du serveur avec une configuration typique :
uvicorn service_demo:app --host 0.0.0.0 --port 8080 --workers 2 --limit-concurrency 500
Stratégies de test sous contraintes extrêmes
Test de concurrence élevée
Utilisez wrk pour simuler un grand nombre de connexions simultanées :
wrk -t8 -c500 -d20s http://localhost:8080/
Test de résistance prolongée
Identifiez les fuites mémoire ou dégradations de performance via une charge soutenue :
# Exécution d'un test sur 15 minutes
wrk -t6 -c300 -d900s http://localhost:8080/
Test de gestion des connexions
Évaluez la capacité du serveur à gérer un pool de connexions avec un script personnalisé :
import httpx
import asyncio
async def retrieve_resource(client, endpoint):
resp = await client.get(endpoint)
return resp.text
async def perform_load_test():
async with httpx.AsyncClient() as client:
tasks = [retrieve_resource(client, 'http://localhost:8080/') for _ in range(800)]
outcomes = await asyncio.gather(*tasks, return_exceptions=True)
valid_count = sum(1 for res in outcomes if not isinstance(res, Exception))
print(f"Requêtes réussies : {valid_count}")
if __name__ == "__main__":
asyncio.run(perform_load_test())
Optimisation des paramètres de concurrence
Les performances dépendent fortement de l'ajustement des options de configuration. Le fichier uvicorn/config.py expose plusieurs paramètres critiques.
Configurations recommandées selon les ressources matérielles
# Serveur avec 4 cœurs
uvicorn app:app --workers 4 --limit-concurrency 800 --backlog 2048
# Serveur avec 8 cœurs
uvicorn app:app --workers 8 --limit-concurrency 1600 --backlog 4096
Configuration via variables d'environnement
export UVICORN_WORKERS=4
export MAX_CONCURRENT_REQUESTS=1000
uvicorn app:app
Analyse des résultats et dépannage
Surveillez ces indicateurs clés durant les tests :
- Débit : requêtes traitées par seconde
- Latence : temps de réponse moyen et percentiles P95/P99
- Taux d'erreur : proportion de réponses HTTP 5xx
- Utilisation des ressources : charge CPU, mémoire, E/S réseau
Problèmes courants et solutions
- Délais d'expiration : ajustez
--timeout-keep-aliveou optimisez le code applicatif. - Fuites mémoire : utilisez le mode
--reloadpour le débogage et surveillez la croissance mémoire. - Goulots d'étranglement CPU : sélectionnez une boucle d'événements plus performante avec
--loop uvloop.
Déploiement en environnement de production
Pour les charges élevées, privilégiez une architecture multi-processus :
# Via Gunicorn avec worker Uvicorn
gunicorn -w 4 -k uvicorn.workers.UvicornWorker app:app
# Directement avec Uvicorn en mode multi-processus
uvicorn app:app --workers 4 --host 0.0.0.0 --port 8080
Monitoring et alertes
Intégrez des outils comme Prometheus et Grafana pour suivre les métriques en temps réel. Configurez des alertes basées sur des seuils critiques, tels que l'utilisation CPU ou le taux d'erreur.
Stratégie d'auto-scaling
Implémentez une scalabilité dynamique basée sur les indicateurs de performance :
- Ajoutez des workers lorsque l'utilisation CPU dépasse 80%.
- Scale up si la file d'attente des requêtes augmente de manière soutenue.
- Réduisez les ressources pendant les périodes creuses pour optimiser les coûts.