Question

J'ai un problème assez simple ici. J'ai besoin de communiquer simultanément avec de nombreux hôtes, mais je n'ai pas vraiment besoin de synchronisation, car chaque requête est assez autonome.

À cause de cela, j'ai choisi de travailler avec des sockets asynchrones plutôt que des threads de spam. Maintenant, j'ai un petit problème:

Le processus asynchrone fonctionne à merveille, mais lorsque je me connecte à 100 hôtes et que je reçois 100 délais d’expiration (timeout = 10 secondes), j’attends 1000 secondes pour découvrir que toutes mes connexions ont échoué.

Existe-t-il un moyen d'obtenir également des connexions de socket non bloquantes? Mon socket est déjà défini sur nonBlocking, mais les appels à connect () bloquent toujours.

Réduire le délai d'attente n'est pas une solution acceptable.

Je le fais en Python, mais j'imagine que le langage de programmation n'a pas vraiment d'importance dans ce cas.

Dois-je vraiment utiliser des threads?

Était-ce utile?

La solution

Vous devez également paralléliser les connexions, car les sockets se bloquent lorsque vous définissez un délai. Sinon, vous ne pouvez pas définir de délai d'expiration et utiliser le module select.

Vous pouvez le faire avec la classe dispatcher du module asyncore . . Jetez un coup d’œil à la exemple de client http de base . Plusieurs instances de cette classe ne se bloqueront pas lors de la connexion. Vous pouvez le faire tout aussi facilement en utilisant des threads, ce qui facilite le suivi des délais de socket, mais puisque vous utilisez déjà des méthodes asynchrones, vous pouvez également rester sur la même piste.

Par exemple, ce qui suit fonctionne sur tous mes systèmes Linux

import asyncore, socket

class client(asyncore.dispatcher):
    def __init__(self, host):
        self.host = host
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, 22))

    def handle_connect(self):
        print 'Connected to', self.host

    def handle_close(self):
        self.close()

    def handle_write(self):
        self.send('')

    def handle_read(self):
        print ' ', self.recv(1024)

clients = []
for i in range(50, 100):
    clients.append(client('cluster%d' % i))

asyncore.loop()

Où dans cluster50 - cluster100, il existe de nombreuses machines qui ne répondent pas ou qui n'existent pas. Cela commence immédiatement à imprimer:

Connected to cluster50
  SSH-2.0-OpenSSH_4.3

Connected to cluster51
  SSH-2.0-OpenSSH_4.3

Connected to cluster52
  SSH-2.0-OpenSSH_4.3

Connected to cluster60
  SSH-2.0-OpenSSH_4.3

Connected to cluster61
  SSH-2.0-OpenSSH_4.3

...

Ceci ne prend cependant pas en compte getaddrinfo, qui doit être bloqué. Si vous rencontrez des problèmes pour résoudre les requêtes DNS, tout doit attendre. Vous devrez probablement rassembler vous-même les requêtes DNS et utiliser les adresses IP dans votre boucle async.

Si vous souhaitez une boîte à outils plus grande qu'un asyncore, consultez Twisted Matrix . C’est un peu lourd, mais c’est la meilleure boîte à outils de programmation réseau pour python.

Autres conseils

Utilisez le module select . Cela vous permet d’attendre la fin des E / S sur plusieurs sockets non bloquants. Voici quelques informations supplémentaires sur select. A partir de la page liée:

  

En C, coder select est assez complexe.   En Python, c'est un morceau de gâteau, mais   c'est assez proche de la version C   que si vous comprenez sélectionnez dans   Python, vous aurez peu de problèmes   avec en C.

ready_to_read, ready_to_write, in_error = select.select(
                  potential_readers, 
                  potential_writers, 
                  potential_errs, 
                  timeout)
  

Vous passez sélectionnez trois listes: la première   contient toutes les sockets que vous pourriez   vouloir essayer de lire; le second tout   les prises que vous voudrez peut-être essayer   écrit à, et le dernier (normalement   laissés vides) ceux que vous voulez   vérifier les erreurs. Vous devriez noter que   une prise peut entrer dans plus d'un   liste. L’appel select bloque, mais   vous pouvez lui donner un temps mort. C'est   généralement une chose sensée à faire -   donnez-lui un long délai d’attente (disons un   minute) sauf si vous avez de bonnes raisons de   faire autrement.

     

En retour, vous obtiendrez trois listes.   Ils ont les prises qui sont   effectivement lisible, inscriptible et en   Erreur. Chacune de ces listes est un sous-ensemble   (éventuellement vide) du correspondant   liste que vous avez passé. Et si vous mettez un   socket dans plusieurs listes d'entrées, il   sera (au plus) dans une sortie   liste.

     

Si une socket est dans la sortie lisible   liste, vous pouvez être   aussi proche de certains que nous arrivons toujours dans cette entreprise   qu'un recv sur ce socket retournera   quelque chose. Même idée pour l'écriture   liste. Vous pourrez envoyer   quelque chose. Peut-être pas tout ce que tu veux,   mais quelque chose vaut mieux que rien.   (En fait, toute personne raisonnablement en bonne santé   socket retournera en écriture - il   signifie simplement tampon réseau sortant   l'espace est disponible.)

     

Si vous avez un " serveur " prise, le mettre   dans la liste potential_readers. Si ça   vient dans la liste lisible, votre   acceptera (presque certainement) le travail.   Si vous avez créé un nouveau socket pour   se connecter à quelqu'un d'autre, le mettre dans le   liste de potentiels. Si cela se présente   dans la liste accessible en écriture, vous avez un   chance décente qu'il s'est connecté.

Malheureusement, il n'y a pas d'exemple de code montrant le bogue, il est donc un peu difficile de voir d'où vient ce bloc.

Il fait quelque chose comme:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(0)
s.connect(("www.nonexistingname.org", 80))

Le module de socket utilise getaddrinfo en interne, ce qui constitue une opération de blocage, en particulier lorsque le nom d'hôte n'existe pas. Un client DNS conforme aux normes attendra quelque temps avant de voir si le nom n'existe pas réellement ou si quelques serveurs DNS lents sont impliqués.

La solution consiste à se connecter aux adresses IP uniquement ou à utiliser un client DNS qui autorise les requêtes non bloquantes, comme pydns .

Utilisez tordu .

C’est un moteur de réseau asynchrone écrit en Python, prenant en charge de nombreux protocoles et que vous pouvez ajouter le vôtre. Il peut être utilisé pour développer des clients et des serveurs. Il ne bloque pas lors de la connexion.

Avez-vous consulté le module asyncore ? Peut-être juste ce dont vous avez besoin.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top