セロリのタスクをasyncio.Task
のように見せるためのラッパーを作成するにはどうすればよいですか?または、Celeryをasyncio
と統合するより良い方法はありますか?
セロリの作成者である@asksol これはこう言った :
Celeryを非同期I/Oフレームワーク上の分散レイヤーとして使用することは非常に一般的です(ヒント:CPUにバインドされたタスクをpreforkワーカーにルーティングすると、イベントループがブロックされなくなります)。
しかし、asyncio
フレームワーク専用のコード例を見つけることができませんでした。
公式サイトに記載されているCeleryバージョン5.0から可能になります。
http://docs.celeryproject.org/en/4.0/whatsnew-4.0.html#preface
- Celeryの次のメジャーバージョンは、Python 3.5のみをサポートします。これは、新しいasyncioライブラリを利用する予定でした。
- Python 2のサポートをドロップすると、大量の互換性コードを削除できます。また、Python 3.5を使用すると、async/await 、asyncio、および同様の概念には、古いバージョンに代わるものはありません。
上記は前のリンクから引用されています。
最善の方法は、バージョン5.0が配布されるのを待つことです!
それまでの間、ハッピーコーディング:)
documentation で説明されているように、run_in_executor
を使用してブロッキング呼び出しをタスクにラップできます。この例では、カスタム timeout も追加しました。
def run_async_task(
target,
*args,
timeout = 60,
**keywords
) -> Future:
loop = asyncio.get_event_loop()
return asyncio.wait_for(
loop.run_in_executor(
executor,
functools.partial(target, *args, **keywords)
),
timeout=timeout,
loop=loop
)
loop = asyncio.get_event_loop()
async_result = loop.run_until_complete(
run_async_task, your_task.delay, some_arg, some_karg=""
)
result = loop.run_until_complete(
run_async_task, async_result.result
)
私がこれを行うために見つけた最もクリーンな方法は、async
関数をasgiref.sync.async_to_sync
でラップすることです( asgiref
から):
from asgiref.sync import async_to_sync
from celery.task import periodic_task
async def return_hello():
await sleep(1)
return 'hello'
@periodic_task(
run_every=2,
name='return_hello',
)
def task_return_hello():
async_to_sync(return_hello)()
ブログ投稿 からこの例を引き出しました。
この簡単な方法はうまくいきました:
import asyncio
from celery import Celery
app = Celery('tasks')
async def async_function(param1, param2):
# more async stuff...
pass
@app.task(name='tasks.task_name', queue='queue_name')
def task_name(param1, param2):
asyncio.run(async_function(param1, param2))