2017-07-22 4 views
1

J'ai la route Sanic suivante:Sanic: comment convertir le contenu de démarquage de manière asynchrone

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) 

Cela fonctionne très bien, mais la conversion prend un temps très long et bloque le point final. Lors de l'analyse comparative, le point de terminaison ne peut traiter que 4 demandes par seconde.

Comme il n'y a pas de bibliothèque asyncio de démarquage, je pensais que je voudrais délester la conversion en un thread séparé pour libérer le blocage de code:

loop = asyncio.get_event_loop() 
content = await loop.run_in_executor(ThreadPoolExecutor(), md.convert(content)) 

Cependant, cela jette un retraçage:

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 

N'est-il pas possible d'utiliser la boucle d'événements depuis Sanic? Existe-t-il d'autres options pour rendre la conversion non bloquante?

Répondre

2

md.convert(content)exécute réellement la fonction. Ce:

content = await loop.run_in_executor(ThreadPoolExecutor(), md.convert(content)) 

est la même que celle-ci:

content = await loop.run_in_executor(ThreadPoolExecutor(), "some HTML") 

Et c'est bien évidemment faux. Vous ne voulez pas exécuter la fonction. Vous voulez passer la fonction; l'exécuteur s'occupera de l'exécuter. Le signature of run_in_executor est

coroutine AbstractEventLoop.run_in_executor(executor, func, *args) 

Il faut donc utiliser ce lieu

content = await loop.run_in_executor(ThreadPoolExecutor(), md.convert, content) 
+0

Ahhh, duh! Merci, ça a fonctionné parfaitement bien sûr. Côté note: c'est encore assez lent, seulement 20 demandes par seconde. Je suppose que c'est le surcoût supplémentaire du thread séparé. – mart1n

+0

Je ne peux rien dire à ce sujet. Lorsque la conversion de la démarque prend un certain temps, cela prend du temps, le multi-thread ne fera que beaucoup. Peut-être qu'il y a une bibliothèque plus rapide autour. Ou un outil non-Python plus rapide. – Tomalak

+0

Compris, merci encore! – mart1n