Tests de charge pour Uvicorn : garantir la robustesse des services en conditions limites

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 :

  1. wrk – Outil de benchmark HTTP haute performance
  2. locust – Framework de test de charge distribué
  3. ab – Utilitaire de benchmark pour serveurs Apache
  4. 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-alive ou optimisez le code applicatif.
  • Fuites mémoire : utilisez le mode --reload pour 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 :

  1. Ajoutez des workers lorsque l'utilisation CPU dépasse 80%.
  2. Scale up si la file d'attente des requêtes augmente de manière soutenue.
  3. Réduisez les ressources pendant les périodes creuses pour optimiser les coûts.

Étiquettes: Uvicorn ASGI concurrent testing load testing Performance Optimization

Publié le 17 juin à 04h58