I think everyone knows what to do with long-running tasks in django: use celery and relax. But what if I want to get benefits of the websockets with aiohttp (or tornado)?
Let's say I have very CPU bound task which can take from a couple of seconds till multiple (5-10) minutes. It looks like pretty good idea to handle this task in websocket loop and notify user about the progress. No ajax requests, very fast response for short tasks.
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
async for msg in ws:
if msg.tp == aiohttp.MsgType.text:
answer_to_the_ultimate_question_of_life_the_universe_and_everything =\
long_running_task(msg.data, NotificationHelper(ws))
ws.send_str(json.dumps({
'action': 'got-answer',
'data': answer_to_the_ultimate_question_of_life_the_universe_and_everything,
}))
return ws
But on the other hand, CPU-bound task served in such way blocks entire thread as I understand. If I have 10 workers and 11 clients who wants to use application, 11th client won't be served until the 1st client's task is done.
Maybe, I should run tasks which look big in celery and tasks which look small in the main loop?
So, my question: is there any good design pattern for serving long-running tasks with async server?
Thanks!
Just run your long-running CPU-bound task by loop.run_in_executor()
and send progress notifications by loop.call_soon_threadsafe()
.
If your job is not CPU but IO bound (sending emails for example) you may create a new task by loop.create_task()
call. It looks like spawning new thread.
If you cannot use fire-and-forget approach you need to use persistent message broker like RabbitMQ (there is https://github.com/benjamin-hodgson/asynqp library for communicating with Rabbit in asyncio way).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With