web-dev-qa-db-fra.com

Ouvriers et fils Gunicorn

En ce qui concerne Gunicorn, je suis conscient qu'il existe différentes classes de travailleurs, mais pour cette conversation, je ne fais que regarder les types de synchronisation et d'async.

De ma compréhension ...

sync
workers = (2 * cpu) + 1
worker_class = sync

async (gevent)
workers = 1
worker_class = gevent
worker_connections = a value (lets say 2000)

Donc (basé sur un système à 4 cœurs) en utilisant des synchroniseurs, je peux avoir un maximum de 9 connexions en parallèle. Avec Async, je peux en avoir jusqu'à 2000, avec les mises en garde fournies avec async.

Questions

  • Alors, où se situent les fils? Puis-je ajouter des threads aux types de travail de synchronisation et d'async?
  • Quelle est la meilleure option pour les travailleurs de la licorne? Dois-je souhaiter placer gunicorn devant une Django API, avec l'exigence de traiter 100s de requêtes en parallèle?
  • Les classes de travail gevent et sync sont-elles sécurisées pour les threads?
38
PacketPimp

Permettez-moi de tenter une réponse. Supposons qu'au début, mon déploiement ne comporte qu'un seul travailleur gunicorn. Cela me permet de gérer une seule demande à la fois. Le travail de mon employé consiste simplement à appeler google.com et à obtenir les résultats de recherche pour une requête. Maintenant, je veux augmenter mon débit. J'ai les options ci-dessous

Conserver un seul travailleur et augmenter le nombre de threads dans ce travailleur

C'est le plus simple. Étant donné que les threads sont plus légers (moins de consommation de mémoire) que les processus, je ne garde qu'un seul travailleur et j'y ajoute plusieurs threads. Gunicorn veillera à ce que le maître puisse ensuite envoyer plus d'une demande au travailleur. Étant donné que le travailleur est multithread, il est capable de gérer 4 demandes. Fantastique. Maintenant, pourquoi aurais-je besoin de plus de travailleurs jamais?

Pour répondre à cela, supposons que je dois travailler sur les résultats de recherche renvoyés par Google. Par exemple, je pourrais aussi vouloir calculer un nombre premier pour chaque requête de résultat. Maintenant, je lie ma charge de travail à un calcul et j'ai rencontré le problème avec le verrou d'interpréteur global de python. Même si j'ai 4 threads, un seul thread peut réellement traiter les résultats à la fois. Cela signifie que pour obtenir de véritables performances parallèles, j'ai besoin de plus d'un travailleur.

Augmenter le nombre de travailleurs mais tous les travailleurs sont à filetage unique

Alors pourquoi j'ai besoin de cela, c'est quand j'ai besoin d'obtenir un véritable traitement parallèle. Chaque travailleur peut parallèlement appeler google.com, obtenir des résultats et effectuer n'importe quel traitement. Tout en parallèle. Fantastique. Mais l'inconvénient est que les processus sont plus lourds, et mon système pourrait ne pas répondre aux demandes des travailleurs croissants d'accomplir le parallélisme. La meilleure solution consiste donc à augmenter le nombre de travailleurs et à ajouter davantage de threads à chaque travailleur.

Augmenter le nombre de travailleurs et chaque travailleur est multithread

Je suppose que cela n'a pas besoin d'explications supplémentaires.

Changer le type de travailleur en Async

Maintenant, pourquoi voudrais-je jamais faire ça? Pour répondre, rappelez-vous que même les threads consomment de la mémoire. Il existe des coroutines (une construction radicale que vous pouvez rechercher) implémentées par la bibliothèque gevent qui vous permettent d'obtenir des threads sans avoir à créer de threads. SO si vous fabriquez votre gunicorn pour utiliser le type de travailleur de gevent, vous obtenez l'avantage de NE PAS avoir à créer de fils dans vos travailleurs. Supposons que vous obtenez des fils sans avoir à créer explicitement leur.

Donc, pour répondre à votre question, si vous utilisez worker_type d'autre chose que Sync, vous n'avez pas besoin d'augmenter le nombre de threads dans votre configuration gunicorn. Vous pouvez le faire, par tous les moyens, mais cela va un peu à l'encontre du but.

J'espère que cela vous a aidé.

J'essaierai également de répondre aux questions spécifiques.

  • Non, l'option filetée n'est pas présente pour la classe de travail Async. Cela doit en fait être clarifié dans la documentation. Vous vous demandez pourquoi cela ne s'est pas produit.

  • Il s'agit d'une question qui nécessite une meilleure connaissance de votre application spécifique. Si le traitement de ces centaines de requêtes parallèles implique uniquement des opérations d'E/S, telles que la récupération à partir de la base de données, la sauvegarde, la collecte de données à partir d'une autre application, vous pouvez utiliser le threaded worker. Mais si ce n'est pas le cas et que vous souhaitez exécuter sur un processeur à n cœurs, car les tâches sont extrêmement liées au calcul, peut-être comme le calcul de nombres premiers, vous devez utiliser le travailleur Sync. Le raisonnement pour Async est légèrement différent. Pour utiliser Async, vous devez vous assurer que votre traitement n'est pas lié au calcul, cela signifie que vous ne pourrez pas utiliser plusieurs cœurs. L'avantage que vous obtenez est que la mémoire que prendrait plusieurs threads ne serait pas là. Mais vous avez d'autres problèmes comme les bibliothèques patché non monkey. Passez à Async uniquement si le travailleur threadé ne répond pas à vos besoins.

  • La synchronisation, les travailleurs non threadés sont la meilleure option si vous voulez une sécurité absolue des threads parmi vos bibliothèques.

69
abhayAndPoorvisDad