Contact salesFree trial
Blog

Python Gevent en pratique : les pièges les plus courants à garder à l'esprit

Pythonperformance
Partager
Cet article est également disponible en Anglais.

Gevent est une bibliothèque réseau Python qui utilise libev et libuv pour sa boucle d'événements et greenlet pour les tâches asynchrones, offrant des abstractions essentielles pour le développement de serveurs.

Historiquement, Eve Online a adopté Stackless Python pour ses serveurs dorsaux, en utilisant des tasklets ou microthreads. Ces tasklets permettaient à des milliers de requêtes de s'exécuter en parallèle au sein d'un seul thread, évitant ainsi les problèmes de performance et de complexité liés aux threads. Stackless Python a ensuite évolué vers Eventlet, inspirant Gevent - une bibliothèque asynchrone idéale pour développer des applications de réseau à haute performance, bénéficiant de la lisibilité et de la rapidité de développement de Python. Ces facteurs ont largement contribué au choix de Gevent par Upsun pour certains services internes comme notre Project API.

Cet article suppose que vous, notre lecteur, êtes familier avec les concepts de base de Python Gevent tels que le multitâche coopératif, les boucles d'événements, les greenlets et l'ordonnancement concurrent. Vous pouvez trouver de bons articles sur ces sujets ici et ici. Cela nous permet de nous concentrer sur les meilleures pratiques et les pièges courants de Gevent, souvent applicables à d'autres bibliothèques asynchrones.

Pièges courants

Rattrapage de singe

Dans Gevent, le "monkey patching" est une technique optionnelle qui remplace les appels bloquants de la bibliothèque standard par des alternatives coopératives, et qui est largement utilisée dans la pratique. Si vous choisissez de ne pas utiliser le monkey patching, vous serez responsable de la gestion des greenlets vous-même, ce qui peut être souhaitable en fonction de votre cas d'utilisation.

Gevent est né à une époque où Python ne supportait pas nativement la programmation asynchrone - pas de mots-clés async ou await. Les applications traditionnelles des serveurs web étaient soit construites en utilisant un modèle pré-fork, soit en s'appuyant sur des threads multiples pour la concurrence. Gevent visait à rendre le code multithread existant concurrent sans s'appuyer sur les threads du système d'exploitation (OS). Parfois, cela peut se faire sans changer une seule ligne de code dans votre application.

Par exemple, dans un environnement multithread, lorsqu'une fonction bloquante comme `socket.recv` est appelée, le noyau gère l'ordonnancement des threads. En revanche, Gevent utilise des threads légers, connus sous le nom de greenlets, qui s'exécutent simultanément à l'intérieur d'un seul thread du système d'exploitation. Pour gérer plusieurs fonctions bloquantes au sein de ce thread unique, Gevent monkey corrige la bibliothèque standard Python. Il s'agit de remplacer les fonctions bloquantes de la bibliothèque par des versions qui utilisent la boucle d'événements pour attendre de manière asynchrone que les opérations se terminent.

Pour que le "monkey patching" soit efficace, certaines règles doivent être strictement respectées :

  • La correction doit être effectuée le plus tôt possible dans le code. Si vous tardez à le faire, une autre partie de votre code risque d'importer un module qui utilise les fonctions originales non corrigées, ce qui entraînera des conflits.
  • La correction des singes doit être effectuée sur le thread principal. Cela permet de s'assurer que les correctifs prennent effet sur tous les greenlets.
  • Le processus de correction doit avoir lieu lorsque l'application est encore monotâche. C'est un point crucial, car la correction des singes de Gevent modifie également la bibliothèque de threads. Si vous avez déjà créé plusieurs threads en utilisant le module de threading non corrigé, des conflits peuvent survenir.

Ces directives ne sont pas seulement des bonnes pratiques ; elles proviennent directement de la documentation officielle du "monkey patching" de Gevent. Le non-respect de ces règles peut entraîner des erreurs imprévisibles et difficiles à diagnostiquer en raison de conflits entre le code corrigé et le code non corrigé.

Blocage des appels

Même si vous avez soigneusement implémenté le monkey patching dans Gevent, un seul greenlet peut toujours bloquer l'ensemble du processus. Cela est particulièrement vrai pour les opérations de lecture/écriture de fichiers. Malgré les avantages du monkey patching, les opérations d'E/S de fichiers sont toujours synchrones sur certains systèmes d'exploitation, ce qui signifie qu'elles peuvent devenir un goulot d'étranglement.

Il existe cependant une stratégie pour contourner cette limitation. Python Gevent offre la possibilité d'effectuer des E/S de fichiers simultanées en utilisant un pool de threads. Cette approche permet de traiter les opérations sur les fichiers sans bloquer le reste de l'application. Ainsi, bien que le "monkey patching" résolve de nombreux problèmes liés à la programmation asynchrone, il est essentiel d'être conscient de ses limites et de savoir comment les contourner pour garantir une application réellement non bloquante.

Bibliothèques tierces

Même avec les capacités de Gevent, il est crucial de noter qu'il ne peut pas rendre les bibliothèques tierces asynchrones si elles n'utilisent pas la bibliothèque standard de Python pour les appels bloquants. Si vous utilisez une bibliothèque externe qui effectue ses propres appels bloquants, il se peut que vous ne découvriez pas le problème avant que votre application ne soit dans un environnement de production.

C'est là que les tests de régression des performances ou les tests de charge peuvent s'avérer utiles. Lorsqu'un greenlet est bloqué sur les E/S, le problème se manifeste généralement par une utilisation sous-optimale de l'unité centrale et un débit stagnant. Dans des circonstances normales, vous ne devriez atteindre les limites de débit que lorsque l'utilisation du CPU est égale ou supérieure à 100 %. Par conséquent, si vous observez une faible utilisation du CPU et aucune augmentation du débit pendant les tests de charge, c'est probablement le signe d'un greenlet bloquant.

Pour résoudre ce problème, vous devrez identifier la bibliothèque tierce problématique et sa fonction de blocage spécifique. Les solutions peuvent consister à passer à une autre bibliothèque ou à modifier la bibliothèque actuelle, bien qu'aucune de ces solutions ne soit simple à mettre en œuvre.

Code à forte intensité de CPU

L'exécution d'un code à forte intensité de CPU dans un seul greenlet Python peut bloquer l'exécution d'autres greenlets, ce qui conduit à ce que l'on appelle la famine du greenlet. Le débogage de ce problème est complexe, car vous devez observer les tendances de changement de contexte pour identifier si certains greenlets monopolisent le temps CPU.

Pour résoudre ce problème, vous disposez de plusieurs options :

  1. Décharger les tâches lourdes de l'unité centrale vers un processus ou un thread séparé, idéalement en utilisant un pool. Cela permet de séparer les tâches liées aux E/S de celles liées à l'UC, et d'éviter qu'elles n'interfèrent les unes avec les autres.
  2. S'il n'est pas possible de séparer les charges de travail - peut-être parce que vous devez exécuter une tâche gourmande en ressources CPU et renvoyer immédiatement le résultat -, cédez explicitement le greenlet pour permettre à d'autres greenlets de s'exécuter.

Il est important de noter que Gevent n'est pas idéal pour les tâches gourmandes en ressources humaines. Cette limitation n'est pas propre à Gevent ; d'autres bibliothèques asynchrones comme Asyncio et Node.js sont également confrontées à des défis similaires.

Sécurité des threads

La sécurité des threads est un concept bien connu dans le monde multithread. Il s'agit de protéger les ressources partagées contre l'accès simultané de plusieurs threads. Alors que les threads traditionnels peuvent changer de contexte à tout moment, ce qui nécessite une résolution manuelle des conflits (à l'aide de mutex), on pourrait supposer que les bibliothèques asynchrones telles que Python Gevent résoudraient automatiquement ces problèmes. Mais ce n'est pas le cas.

Même si les bibliothèques asynchrones comme Gevent n'ont pas le même niveau de complexité que les threads préemptifs traditionnels, elles impliquent toujours des ressources partagées auxquelles accèdent différentes greenlets. Cela peut conduire à des problèmes subtils, parfois difficiles à détecter.

Pensez au code suivant :

def withdraw(self, amount) : if self.balance >= amount : withdraw_from_db(amount) self.balance -= amount

Supposons que le code ci-dessus soit appelé par plusieurs greenlets et que withdraw_from_db() soit un appel qui donne lieu à une exécution. Puisque self.balance est une ressource partagée, il est fort possible que le retrait se produise plusieurs fois même si la balance n'est pas suffisante. Le problème est que la balance est lue et qu'un changement de contexte peut se produire et que le second self.balance que nous lisons peut ne pas être le même que celui que nous avons lu en premier.

Il existe un support natif pour les verrous dans Gevent. Mais je vous suggère de concevoir la manière dont vous accédez à vos ressources partagées de manière à minimiser ce problème autant que possible, car chaque fois que vous utilisez un verrou, cela bloque l'exécution d'autres greenlets, ce qui est exactement l'opposé de la simultanéité. De plus, il est très probable que vous utilisiez le verrou lorsque vous avez un appel bloquant, mais cela pourrait être une recette pour un désastre. Par exemple, vous pouvez peut-être mettre en œuvre la logique de retrait en utilisant une transaction de base de données et supprimer self.balance du code. Il peut y avoir d'autres façons de contourner ce problème, il suffit d'y réfléchir soigneusement.

Conclusion

Comme tout framework, Gevent a ses hauts et ses bas. Bien qu'il offre l'avantage d'une programmation asynchrone avec moins de complexité que le multithreading traditionnel, ce n'est pas une solution miracle à tous les problèmes de concurrence. Il est essentiel d'être vigilant quant à ces limitations et aux pièges possibles afin d'utiliser Gevent le plus efficacement possible dans vos applications. Faites-nous part des pièges que vous avez rencontrés avec Gevent sur Discord.

Votre meilleur travail
est à l'horizon

Essai gratuit
Discord
© 2025 Platform.sh. All rights reserved.