2009-03-11 7 views
9

Je lisais récemment this document qui énumère un certain nombre de stratégies qui pourraient être employées pour implémenter un serveur socket. A savoir, ils sont:Écrire un serveur basé sur socket en Python, stratégies recommandées?

  1. Serve de nombreux clients chaque fil, et utiliser bloquante E/S et la notification de préparation déclenché par niveau
  2. Serve de nombreux clients chaque fil, et utiliser bloquante E/S et la préparation de notification de changement
  3. Serve de nombreux clients à chaque thread de serveur, et utiliser E/S asynchrones
  4. servir un client à chaque thread de serveur, et utiliser le blocage d'E/S
  5. Construire le code du serveur dans le noyau

Maintenant, j'apprécierais un indice sur lequel devrait être utilisé dans CPython, que nous savons a quelques bons points, et quelques mauvais points. Je suis surtout intéressé par les performances sous forte concurrence, et oui un certain nombre d'implémentations actuelles sont trop lentes. Donc, si je peux commencer par le plus facile, "5" est sorti, car je ne vais pas pirater quoi que ce soit dans le noyau.

"4" Apparemment, il doit être éteint à cause du GIL. Bien sûr, vous pourriez utiliser le multiprocessing à la place des threads ici, et cela donne un coup de pouce significatif. Bloquer les E/S a également l'avantage d'être plus facile à comprendre.

Et voici ma connaissance faiblit un peu:

« 1 » est traditionnelle select ou un sondage qui pourrait être trivialement combiné avec multitraitement.

« 2 » est la notification de changement de préparation, utilisé par le plus récent epoll et KQueue

« 3 » Je ne suis pas sûr qu'il ya des implémentations du noyau pour ce qui ont wrappers Python. Donc, en Python, nous avons un sac de bons outils comme Twisted. Peut-être sont-ils une meilleure approche, même si j'ai comparé Twisted et l'ai trouvé trop lent sur une machine à plusieurs processeurs. Peut-être avoir 4 torsades avec un équilibreur de charge pourrait le faire, je ne sais pas. Tout avis sera le bienvenu.

Répondre

7

asyncore est fondamentalement "1" - Il utilise select en interne, et vous avez juste un thread traitant toutes les demandes. Selon les documents, il peut également utiliser poll. (EDIT: Supprimé Twisted référence, je pensais qu'il utilisait asyncore, mais j'avais tort).

"2" pourrait être implémenté avec python-epoll (Il suffit de googler - jamais vu auparavant). EDIT: (à partir des commentaires) En python 2.6 le select module a des intégrations epoll, kqueue et kevent (sur les plates-formes supportées). Vous n'avez donc pas besoin de bibliothèques externes pour effectuer un service de périphérie.

N'excluez pas "4", car le GIL sera supprimé lorsqu'un thread est en train de faire ou d'attendre des opérations d'E/S (la plupart du temps probablement). Cela n'a pas de sens si vous avez un grand nombre de connexions bien sûr. Si vous avez beaucoup de travail à faire, alors python peut ne pas avoir de sens avec l'un de ces schémas.

Pour la flexibilité peut-être regarder Twisted?

En pratique, votre problème se résume à la quantité de traitement que vous allez faire pour vos demandes. Si vous avez beaucoup de traitement et que vous avez besoin de tirer parti du fonctionnement parallèle multi-cœur, vous aurez probablement besoin de plusieurs processus. D'un autre côté, si vous avez juste besoin d'écouter sur beaucoup de connexions, alors sélectionnez ou epoll, avec un petit nombre de threads devrait fonctionner.

+0

Je pense que epoll est dans le stdlib dans 2.6+, et easy_installable pour 2.5. Le paquet s'appelle select-something. Désolé pour le vague. –

+0

Twisted peut également utiliser epoll. En fait, Twisted transforme toutes les API de notification d'événement prises en charge en une API uniforme qu'elle vous présente. Donc, si le meilleur que la plate-forme peut faire est de sélectionner, votre application utilise select. S'il a epoll, votre application utilise epoll. Tout est transparent pour vous. –

+0

Il est orthographié 'asyncore' – new123456

1

http://docs.python.org/library/socketserver.html#asynchronous-mixins

Comme pour les machines multi-processeurs (multi-core). Avec CPython en raison de GIL, vous aurez besoin d'au moins un processus par cœur, à l'échelle. Comme vous dites que vous avez besoin de CPython, vous pouvez essayer de comparer cela avec ForkingMixIn. Avec Linux 2.6 pourrait donner des résultats intéressants. L'autre manière est d'utiliser Stackless Python. C'est how EVE solved it. Mais je comprends que ce n'est pas toujours possible.

+0

Merci, mais avez-vous benchmarkée ces choses? Ils sont lents. Je n'inventerais pas la roue si je n'avais pas à le faire. –

+0

+1 stackless/EVE, mais j'ai dit CPython –

1

J'aime la réponse de Douglas, mais en aparté ...

Vous pouvez utiliser un fil/processus de distribution centralisé qui écoute les notifications de préparation à l'aide select et les délégués à un pool de threads de travail/processes pour aider à accomplir vos objectifs de parallélisme. Comme Douglas l'a mentionné, cependant, le GIL ne sera pas tenu pendant les plus longues opérations d'E/S (puisque aucune chose Python-API ne se passe), donc si c'est la latence de la réponse, vous pouvez essayer de déplacer le portions critiques de votre code à l'API CPython.

2

Puis-je suggérer des liens supplémentaires? Est une bibliothèque multi-plateforme pour la programmation orientée réseau, basée sur la coroutine, utilisant les générateurs améliorés de python 2.5. Sur la page principale du projet cogen il y a des liens vers plusieurs projets ayant un but similaire.

3

Que diriez-vous de "fourchette"? (Je suppose que c'est ce que fait ForkingMixIn) Si les requêtes sont gérées dans une architecture "shared nothing" (autre que DB ou système de fichiers), fork() démarre assez rapidement sur la plupart des nixes, et vous n'avez pas à vous inquiéter à propos de tous les bugs stupides et les complications du filetage.

Les fils sont une maladie de conception qui nous est imposée par les systèmes d'exploitation avec des processus trop lourds, à mon humble avis. Le clonage d'une table de pages avec des attributs de copie sur écriture semble un petit prix, surtout si vous utilisez un interpréteur.

Désolé, je ne peux pas être plus précis, mais je suis plus d'un Perl transitionnés à Ruby programmeur (quand je ne suis pas escrimé sur des masses de Java au travail)


Mise à jour : J'ai finalement fait quelques temps sur le fil contre la fourche dans mon "temps libre". Check it out:

http://roboprogs.com/devel/2009.04.html

ELARGI: http://roboprogs.com/devel/2009.12.html

+0

En outre, vous pouvez récupérer le code des autres modules que vous savez utiliser avant de démarrer les processus enfants. Cela les empêchera d'être jetés (JIT-ed, peu importe) encore et encore. Deuxièmement, gardez les données dans le parent petit, utilisez "exit" comme un super garbage collector. – Roboprog

3

Un sollution est gevent. Gevent maries une interrogation d'événement basée sur libevent avec une commutation de tâche coopérative légère implémentée par greenlet. Ce que vous obtenez est toute la performance et l'évolutivité d'un système d'événements avec l'élégance et le modèle simple de blocage de la programmation IO.

(Je ne sais pas ce que la convention SO à bien répondre aux questions est vraiment vieux, mais décidé que je serais encore ajouter mes 2 cents)

Questions connexes