sanic: как преобразовать контент уценки асинхронно
У меня есть следующий маршрут Sanic:
md = Markdown()
@app.route('/md_file')
async def md_file(request):
async with aiofiles.open('./file.md')) as f:
content = await f.read()
content = md.convert(content)
return html(content)
Это работает просто отлично, но преобразование занимает очень много времени и блокирует конечную точку. При сравнительном тестировании конечная точка может обрабатывать только 4 запроса в секунду.
Так как нет библиотеки асинхронной разметки, я решил, что я перенесу преобразование в отдельный поток, чтобы освободить код блокировки:
loop = asyncio.get_event_loop()
content = await loop.run_in_executor(ThreadPoolExecutor(), md.convert(content))
Тем не менее, это создает трассировку:
2017-07-22 12:02:24 - (sanic)[ERROR]: Traceback (most recent call last):
File "/home/user/app/venv/lib64/python3.5/site-packages/sanic/app.py", line 471, in handle_request
response = await response
File "app.py", line 127, in blog_posts
content = await loop.run_in_executor(ThreadPoolExecutor(), md.convert(content))
File "uvloop/future.pyx", line 241, in __await__ (uvloop/loop.c:110786)
File "uvloop/future.pyx", line 432, in uvloop.loop.BaseTask._fast_wakeup (uvloop/loop.c:113980)
File "uvloop/future.pyx", line 101, in uvloop.loop.BaseFuture._result_impl (uvloop/loop.c:108900)
File "/opt/rh/rh-python35/root/usr/lib64/python3.5/concurrent/futures/thread.py", line 55, in run
result = self.fn(*self.args, **self.kwargs)
TypeError: 'str' object is not callable
Разве невозможно использовать цикл событий изнутри Sanic? Есть ли другие варианты, чтобы сделать преобразование неблокирующим?
1 ответ
md.convert(content)
на самом деле запускает функцию. Это:
content = await loop.run_in_executor(ThreadPoolExecutor(), md.convert(content))
так же, как это:
content = await loop.run_in_executor(ThreadPoolExecutor(), "some HTML")
И это совершенно очевидно неправильно. Вы не хотите запускать функцию. Вы хотите передать функцию; исполнитель справится с его запуском. Подписьrun_in_executor
является
coroutine AbstractEventLoop.run_in_executor(executor, func, *args)
Так что используйте это вместо
content = await loop.run_in_executor(ThreadPoolExecutor(), md.convert, content)