web-dev-qa-db-fra.com

Asyncio deux boucles pour différentes tâches d'E/S?

J'utilise le module Asyncio Python3 pour créer une application d'équilibrage de charge. J'ai deux lourdes tâches IO:

  • Un module d'interrogation SNMP, qui détermine le meilleur serveur possible
  • Un module de type "proxy", qui équilibre les requêtes adressées au serveur sélectionné.

Les deux processus vont fonctionner à l'infini, sont indépendants l'un de l'autre et ne doivent pas être bloqués par l'autre.

Je ne peux pas utiliser une boucle d'événements car ils se bloqueraient. Y a-t-il un moyen d'avoir deux boucles d'événements ou dois-je utiliser le multithreading/le traitement?

J'ai essayé d'utiliser asyncio.new_event_loop () mais je n'ai pas réussi à le faire fonctionner.

12
brunoop

Répondant à ma propre question pour poster ma solution:

J'ai fini par créer un thread et une nouvelle boucle d'événement à l'intérieur du thread pour le module d'interrogation, de sorte que chaque module s'exécute désormais dans une boucle différente. Ce n'est pas une solution parfaite, mais c'est le seul qui ait du sens pour moi (je voulais éviter les threads, mais comme ce n'est qu'un ...). Exemple:

import asyncio
import threading


def worker():
    second_loop = asyncio.new_event_loop()
    execute_polling_coroutines_forever(second_loop)
    return

threads = []
t = threading.Thread(target=worker)
threads.append(t)
t.start()

loop = asyncio.get_event_loop()
execute_proxy_coroutines_forever(loop)

Asyncio exige que chaque boucle exécute ses coroutines dans le même thread. En utilisant cette méthode, vous avez une boucle d'événement pour chaque thread, et ils sont totalement indépendants: chaque boucle exécute ses coroutines sur son propre thread, ce qui ne pose pas de problème. Comme je l'ai dit, ce n'est probablement pas la meilleure solution. , mais cela a fonctionné pour moi.

18
brunoop

L’intérêt de asyncio est que vous pouvez exécuter simultanément plusieurs milliers de tâches lourdes en E/S, de sorte que vous n’avez pas besoin de Threads, c’est exactement ce pour quoi asyncio est conçu. Il suffit d’exécuter les deux coroutines (SNMP et proxy) dans la même boucle et le tour est joué. Vous devez les rendre tous les deux disponibles pour la boucle d’événements AVANT d’appeler loop.run_forever(). Quelque chose comme ça:

import asyncio

async def snmp():
    print("Doing the snmp thing")
    await asyncio.sleep(1)

async def proxy():
    print("Doing the proxy thing")
    await asyncio.sleep(2)

async def main():
    while True:
        await snmp()
        await proxy()

loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()

Je ne connais pas la structure de votre code, donc les différents modules peuvent avoir leur propre boucle infinie ou quelque chose, dans ce cas, vous pouvez exécuter quelque chose comme ceci:

import asyncio

async def snmp():
    while True:
        print("Doing the snmp thing")
        await asyncio.sleep(1)

async def proxy():
    while True:
        print("Doing the proxy thing")
        await asyncio.sleep(2)

loop = asyncio.get_event_loop()
loop.create_task(snmp())
loop.create_task(proxy())
loop.run_forever()

N'oubliez pas que snmp et proxy doivent être des coroutines (async def) écrites de manière asyncologique. asyncio ne fera pas un blocage simple des fonctions Python soudainement "async".

Dans votre cas particulier, je soupçonne que vous êtes un peu confus (sans vouloir offenser!), Car des modules asynchrones bien écrits ne se bloqueront jamais dans la même boucle. Si tel est le cas, vous n'avez pas du tout besoin de asyncio et vous n'avez tout simplement qu'à exécuter l'un d'eux dans une Thread séparée sans traiter aucun élément asyncio.

4
kissgyorgy

La boucle d'événement Asyncio est un seul thread en cours d'exécution et ne fonctionnera pas en parallèle, c'est sa conception. La chose la plus proche à laquelle je peux penser utilise asyncio.wait.

from asyncio import coroutine
import asyncio

@coroutine
def some_work(x, y):
    print("Going to do some heavy work")
    yield from asyncio.sleep(1.0)
    print(x + y)

@coroutine
def some_other_work(x, y):
    print("Going to do some other heavy work")
    yield from asyncio.sleep(3.0)
    print(x * y)



if __== '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait([asyncio.async(some_work(3, 4)), 
                            asyncio.async(some_other_work(3, 4))]))
    loop.close()

une autre méthode consiste à utiliser asyncio.gather() - elle renvoie des résultats futurs à partir de la liste donnée des contrats à terme.

tasks = [asyncio.Task(some_work(3, 4)), asyncio.Task(some_other_work(3, 4))]
loop.run_until_complete(asyncio.gather(*tasks))
1
Nihal Sharma

Mais j'ai utilisé ça comme ça, mais toujours son synchrone no async:

def main(*args):    
    loop = get_event_loop()
    coro = asyncio.start_server(handle_echo, '127.0.0.1', 50008,loop=loop)
    srv = loop.run_until_complete(coro)        
    loop.run_forever()    

@asyncio.coroutine
def handle_echo(reader, writer):
    data = yield from reader.read(500)
    message = data.decode(encoding='utf-8')            

    nameindex=('name="calculator2"' in message)
    if nameindex:
        time.sleep(5)
        writer.write("Content-Length: 1\r\n\r\n2".encode())
        yield from writer.drain()
    else:
        writer.write("Content-Length: 1\r\n\r\n1".encode())
        yield from writer.drain()



    print("Close the client socket")
    writer.close()

si la valeur reçue contient (name = "calculator2") J'attends 5 secondes sinon, répondez et écrivez les données immédiatement. Mais lorsque vous les testez, envoyez d'abord les données au serveur avec (nom) = "calculatrice2") et les données suivantes sans (nom = "calculatrice2"), mais les données suivantes seront traitées après 5 secondes de la première et ensuite, les 2ièmes données seront traitées.

son séquentiel. qu'est-ce qui ne va pas avec cela? et dans l'autre sens, comment dois-je obtenir l'adresse IP et le port connectés au client?

1
Hamed_gibago

Si le serveur proxy est en cours d'exécution tout le temps, il ne peut pas basculer. Le proxy écoute les demandes des clients et les rend asynchrones, mais l'autre tâche ne peut pas s'exécuter car celle-ci est en service pour toujours. 

Si le proxy est une coroutine et empêche le pollueur SNMP (jamais d'attendre), les demandes du client ne sont-elles pas affamées?

chaque coroutine fonctionnera pour toujours, ils ne finiront pas

Cela devrait aller, aussi longtemps qu'ils le font await/yield from. Le serveur echo fonctionnera également pour toujours, cela ne signifie pas que vous ne pouvez pas exécuter plusieurs serveurs (sur des ports différents) dans la même boucle.

0
Markus Bergkvist