web-dev-qa-db-ja.com

異なるI / Oタスク用のAsyncio 2ループ?

Python3 Asyncioモジュールを使用して、負荷分散アプリケーションを作成しています。 2つの重いIOタスク:

  • 最高のサーバーを決定するSNMPポーリングモジュール
  • 選択したサーバーへの請願のバランスをとる「プロキシのような」モジュール。

両方のプロセスは永久に実行され、互いに独立しており、他のプロセスによってブロックされるべきではありません。

1つのイベントループを使用できないのは、お互いをブロックするためです。2つのイベントループを作成する方法はありますか、それともマルチスレッド/処理を使用する必要がありますか?

私はasyncio.new_event_loop()を使ってみましたが、うまくいくことができませんでした。

26
brunoop

自分の質問に答えて私の解決策を投稿する:

結局、ポーリングモジュールのスレッド内にスレッドと新しいイベントループを作成したので、すべてのモジュールが異なるループで実行されます。これは完璧な解決策ではありませんが、私にとって意味のある唯一の解決策です(スレッドを避けたかったのですが、それは1つだけなので...)。例:

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では、すべてのループが同じスレッドでコルーチンを実行する必要があります。このメソッドを使用すると、スレッドごとに1つのイベントループがあり、それらは完全に独立しています。すべてのループが独自のスレッドでコルーチンを実行するため、問題ありません。言ったように、それはおそらく最良の解決策ではありませんが、私にとってはうまくいきました。

20
brunoop

asyncioの要点は、何千ものI/O負荷の高いタスクを同時に実行できることです。したがって、Threadsはまったく必要ありません。これが、asyncioの目的です。 2つのコルーチン(SNMPとプロキシ)を同じループで実行するだけです。 loop.run_forever()を呼び出す前に、両方をイベントループで使用できるようにする必要があります。このようなもの:

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()

コードの構造がわからないので、さまざまなモジュールに独自の無限ループなどがある場合があります。この場合、次のように実行できます。

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()

snmpproxyはどちらも非同期に対応した方法で記述されたコルーチン(async def)である必要があります。 asyncioは単純なブロッキングを行いませんPython関数は突然「非同期」になります。

あなたの特定のケースでは、よく書かれた非同期モジュールが同じループでお互いをブロックすることは決してないので、少し混乱していると思います(問題はありません!)。この場合、asyncioはまったく不要で、Threadの要素を処理せずに、それらの1つを別のasyncioで実行するだけです。

11
kissgyorgy

Asyncioイベントループは実行中の単一スレッドであり、並行して何も実行せず、その設計方法です。私が考えることができる最も近いものは、_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 __name__ == '__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()
_

別の方法はasyncio.gather()を使用することです-指定されたフューチャーのリストからフューチャーの結果を返します。

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

しかし、私はこれを次のように使用しましたが、それでも同期は非同期です:

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()

受け取った値に(name = "calculator2")が含まれている場合は、5秒待機します。そうでない場合は、応答してすぐにデータを書き込みます。しかし、それをテストするときは、最初に(name = "calculator2")を含むデータをサーバーに送信し、(name = "calculator2")を含まない次のデータを送信しますが、最初の5秒後に次のデータが処理され、その後2番目のデータが処理されます。

その順次。何が悪いの?逆に、クライアントに接続されたIPとポートを取得するにはどうすればよいですか?

1
Hamed_gibago

プロキシー・サーバーが常に稼働している場合、前後に切り替えることはできません。プロキシはクライアントリクエストをリッスンして非同期にしますが、他のタスクは永久に機能しているため、実行できません。

プロキシがコルーチンであり、SNMPポーラーが不足している(待機しない)場合、クライアントの要求も不足していませんか?

すべてのコルーチンは永久に実行され、終了しません

彼らがそうである限り、これは問題ないはずですawait/yield fromecho server も永久に実行されます。これは、同じループで複数のサーバーを(異なるポートで)実行できないことを意味するものではありません。

0