{"id":865,"date":"2026-01-19T09:27:00","date_gmt":"2026-01-19T08:27:00","guid":{"rendered":"https:\/\/almic.fr\/blog\/?p=865"},"modified":"2026-01-28T06:11:06","modified_gmt":"2026-01-28T05:11:06","slug":"asgi-hypercorn","status":"publish","type":"post","link":"https:\/\/almic.fr\/blog\/2026\/01\/19\/asgi-hypercorn\/","title":{"rendered":"Serveurs ASGI &#038; utilisation d\u2019Hypercorn"},"content":{"rendered":"<p style=\"text-align: justify;\">Pour d\u00e9velopper ou d\u00e9ployer une application web asynchrone en Python, on a besoin d\u2019un serveur ASGI. Les principales options sont Uvicorn avec Gunicorn, Daphne, Hypercorn et Granian. Apr\u00e8s un tour d\u2019horizon de ces diff\u00e9rents serveurs, j\u2019explique comment j\u2019utilise Hypercorn.<\/p>\n<p><!--more--><\/p>\n<h2>Contexte : ASGI vs WSGI<\/h2>\n<p style=\"text-align: justify;\">WSGI (Web Server Gateway Interface) est une interface synchrone pour les applications web en Python (qui utilisent par exemple Flask, ou Django sans les channels). WSGI a un mod\u00e8le bloquant, chaque requ\u00eate monopolise un thread et doit \u00eatre enti\u00e8rement trait\u00e9e avant que le thread puisse traiter la suivante. \u00c7a entra\u00eene des d\u00e9lais lors des requ\u00eates vers une base de donn\u00e9es, ou d\u2019autres appels \u00e0 des services externes.<\/p>\n<p style=\"text-align: justify;\">ASGI (Asynchronous Server Gateway Interface) est une interface asynchrone, con\u00e7ue pour pallier les limitations de WSGI. ASGI permet d\u2019utiliser la syntaxe async\/await, et c\u2019est une interface qui est particuli\u00e8rement adapt\u00e9e pour g\u00e9rer des connexions persistantes (comme les WebSockets).<\/p>\n<p style=\"text-align: justify;\">Ce n\u2019est pas le r\u00f4le du framework ASGI (FastAPI, Starlette directement, Quart, etc) de g\u00e9rer les connexions r\u00e9seau. Pour \u00e7a, on utilise un serveur ASGI.<\/p>\n<h2>Tour d\u2019horizon<\/h2>\n<p><center><\/p>\n<h4>1. Uvicorn avec Gunicorn<\/h4>\n<p><\/center><\/p>\n<p style=\"text-align: justify;\">Uvicorn est un serveur ASGI minimaliste et tr\u00e8s rapide, con\u00e7u pour ASGI. Il s\u2019appuie sur uvloop (un event loop tr\u00e8s performant) et httptools (un parser HTTP bas niveau). Par d\u00e9faut, il affiche un log digeste sur sa sortie standard, ce qui est un bonus notable pour le d\u00e9veloppement.<\/p>\n<p style=\"text-align: justify;\">Par contre, la gestion des processus n\u2019est pas son fort. Pour am\u00e9liorer ce point, il est couramment utilis\u00e9 en combinaison avec Gunicorn (un serveur WSGI historique).<\/p>\n<p><center><\/p>\n<h4>2. Daphne<\/h4>\n<p><\/center><\/p>\n<p style=\"text-align: justify;\">Daphne a \u00e9t\u00e9 d\u00e9velopp\u00e9 pour servir les channels Django. Comme il n\u2019a pas de support pour uvloop, il est un peu moins performant qu\u2019Uvicorn et Hypercorn. \u00c0 noter que c\u2019est le premier serveur ASGI \u00e0 avoir \u00e9t\u00e9 publi\u00e9.<\/p>\n<p><center><\/p>\n<h4>3. Hypercorn<\/h4>\n<p><\/center><\/p>\n<p style=\"text-align: justify;\">Hypercorn est inspir\u00e9 de Gunicorn, mais d\u00e8s le d\u00e9part il a \u00e9t\u00e9 con\u00e7u pour ASGI. Il faisait initialement partie du framework Quart, avant d\u2019\u00eatre s\u00e9par\u00e9 pour devenir un projet autonome. Il supporte uvloop, mais aussi asyncio et trio. Il peut \u00e9galement servir des applications WSGI.<\/p>\n<p><center><\/p>\n<h4>4. Granian<\/h4>\n<p><\/center><\/p>\n<p style=\"text-align: justify;\">Granian est \u00e9crit en Rust (Hyper + Tokio), avec des bindings Python. Il peut servir des applications ASGI ou WSGI. Correctement configur\u00e9, Granian est tr\u00e8s rapide, mais un param\u00e9trage fin peut \u00eatre n\u00e9cessaire. Certaines applications peuvent ne pas profiter de toutes les optimisations de Granian, \u00e0 cause de probl\u00e8mes de compatibilit\u00e9 avec leurs d\u00e9pendances.<\/p>\n<h2>Utilisation d\u2019Hypercorn<\/h2>\n<p style=\"text-align: justify;\">J\u2019ai choisi Hypercorn, parce qu\u2019avec un seul serveur je peux g\u00e9rer ASGI et WSGI, avec HTTP\/2 et TLS int\u00e9gr\u00e9s. Mais je vais garder un \u0153il sur Granian, car le <a href=\"https:\/\/github.com\/emmett-framework\/granian\/blob\/master\/benchmarks\/vs.md\">benchmark publi\u00e9 par ses devs<\/a> montre des performances tr\u00e8s prometteuses.<\/p>\n<p style=\"text-align: justify;\">Comme c\u2019est un service qui sera accessible publiquement, je pr\u00e9f\u00e8re utiliser le paquet Debian.<\/p>\n<pre># apt install python3-hypercorn<\/pre>\n<p style=\"text-align: justify;\">Vous pouvez regarder <a href=\"https:\/\/almic.fr\/blog\/2016\/01\/07\/certificats-gratuits-avec-lets-encrypt\/\">ce tutoriel<\/a> pour mettre en place des certificats Let\u2019s Encrypt sur un syst\u00e8me Debian.<\/p>\n<p style=\"text-align: justify;\">Il n\u2019y a pas de raison de faire tourner Hypercorn en root, donc on on lui cr\u00e9e un utilisateur d\u00e9di\u00e9. Il faut donner \u00e0 cet utilisateur les droits sur les dossiers du certificat, et sur le dossier des logs.<\/p>\n<pre>\n# adduser hypercorn\n# adduser hypercorn certifs\n# chgrp -R certifs \/etc\/letsencrypt\/live\/domain.tld\n# chmod -R g+r \/etc\/letsencrypt\/live\/domain.tld\n# chgrp -R certifs \/etc\/letsencrypt\/archive\/domain.tld\n# chmod -R g+r \/etc\/letsencrypt\/archive\/domain.tld\n# mkdir \/var\/log\/hypercorn\n# chown hypercorn:hypercorn \/var\/log\/hypercorn\n<\/pre>\n<p style=\"text-align: justify;\">Il faut aussi g\u00e9rer les droits apr\u00e8s les renouvellements automatiques du certificat Let\u2019s Encrypt. \u00c9ditez <i>\/etc\/crontab<\/i> pour ajouter cette ligne, en choisissant un horaire o\u00f9 le serveur est g\u00e9n\u00e9ralement peu charg\u00e9 :<\/p>\n<pre>M H     3 * *   root    \/usr\/local\/bin\/certif_hypercorn.sh<\/pre>\n<p style=\"text-align: justify;\">Cr\u00e9ez le fichier <i>\/usr\/local\/bin\/certif_hypercorn.sh<\/i> :<\/p>\n<pre>\n#!\/bin\/sh\n\nchgrp -R certifs \/etc\/letsencrypt\/archive\/domain.tld\nchmod -R g+r \/etc\/letsencrypt\/archive\/domain.tld\nkillall -9 hypercorn\nsleep 5\n\/usr\/local\/boot.d\/hypercorn.sh\n<\/pre>\n<p style=\"text-align: justify;\">Pour faire vos premiers tests, lancez cette commande :<\/p>\n<pre>\n% hypercorn -k uvloop -w 4 --bind LAN_IP:60444 --certfile \/path\/to\/cert.pem --keyfile \/path\/to\/key.pem --access-logfile - --reload main:app\n<\/pre>\n<p style=\"text-align: justify;\">Une fois que tout marche, il faut configurer nginx (par exemple), en cr\u00e9ant un site qui servira de proxy inverse vers localhost:60444. Cr\u00e9ez le fichier <i>\/etc\/nginx\/sites-available\/domain.tld<\/i> :<\/p>\n<pre>\nserver {\n    server_name domain.tld;\n    listen 443 ssl;\n    ssl_certificate \/etc\/letsencrypt\/live\/domain.tld\/fullchain.pem;\n    ssl_certificate_key \/etc\/letsencrypt\/live\/domain.tld\/privkey.pem;\n\n    # Serve static files directly\n    location \/static\/ {\n        alias \/home\/bot\/harmonia\/harmonia\/display_history\/static\/;\n        access_log off;\n        expires 30d;\n        add_header Cache-Control \"public\";\n    }\n    location \/attachments\/ {\n        alias \/home\/bot\/images\/test_images\/;\n        access_log off;\n        expires 30d;\n        add_header Cache-Control \"public\";\n    }\n\n    # Everything else goes to Hypercorn\n    location \/ {\n        include proxy_params;\n        proxy_pass https:\/\/127.0.0.1:60444;\n    }\n}\n\nserver {\n    server_name domain.tld;\n    listen 80;\n    return 302 https:\/\/domain.tld\/$request_uri;\n}\n<\/pre>\n<p style=\"text-align: justify;\">On active ce nouveau site et on relance nginx :<\/p>\n<pre>\n# cd \/etc\/nginx\/sites-enabled\n# ln -s ..\/sites-available\/domain.tld\n# systemctl reload nginx.service\n<\/pre>\n<p style=\"text-align: justify;\">Ensuite, on cr\u00e9e une unit\u00e9 SystemD, afin qu\u2019Hypercorn d\u00e9marre en m\u00eame temps que le syst\u00e8me. Cr\u00e9ez le fichier <i>\/etc\/systemd\/system\/hypercorn.service<\/i> :<\/p>\n<pre>\n[Unit]\nRequires=network-online.target\nAfter=network-online.target\n\n[Install]\nWantedBy=multi-user.target\n\n[Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=\/usr\/local\/boot.d\/hypercorn.sh\n<\/pre>\n<p style=\"text-align: justify;\">Cr\u00e9ez le fichier <i>\/usr\/local\/boot.d\/hypercorn.sh<\/i> :<\/p>\n<pre>\n#!\/bin\/sh\n\nsu -l hypercorn -c \"cd \/path\/to\/harmonia\/display_history && hypercorn -k uvloop -w 4 --bind localhost:60444 --certfile \/path\/to\/cert.pem --keyfile \/path\/to\/key.pem --access-logfile \/var\/log\/hypercorn\/domain-access.log --error-logfile \/var\/log\/hypercorn\/domain-error.log Main:Display_history\"\n<\/pre>\n<p style=\"text-align: justify;\">On donne les droits d\u2019ex\u00e9cution aux deux scripts :<\/p>\n<pre>\n# chmod 744 \/usr\/local\/boot.d\/hypercorn.sh\n# chmod 744 \/usr\/local\/bin\/certif_hypercorn.sh\n<\/pre>\n<p style=\"text-align: justify;\">Et pour finir, on active l\u2019unit\u00e9 qu\u2019on vient de cr\u00e9er :<\/p>\n<pre>systemctl enable hypercorn.service<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Pour d\u00e9velopper ou d\u00e9ployer une application web asynchrone en Python, on a besoin d\u2019un serveur ASGI. Les principales options sont Uvicorn avec Gunicorn, Daphne, Hypercorn et Granian. Apr\u00e8s un tour d\u2019horizon de ces diff\u00e9rents serveurs, j\u2019explique comment j\u2019utilise Hypercorn.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-865","post","type-post","status-publish","format-standard","hentry","category-hebergement","radius"],"_links":{"self":[{"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/posts\/865","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/comments?post=865"}],"version-history":[{"count":38,"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/posts\/865\/revisions"}],"predecessor-version":[{"id":908,"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/posts\/865\/revisions\/908"}],"wp:attachment":[{"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/media?parent=865"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/categories?post=865"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/almic.fr\/blog\/wp-json\/wp\/v2\/tags?post=865"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}