2014-05-21 2 views
6

Mon post-traitement de modèle est en utilisant le signal post_save:signaux asynchrones avec asyncio

from django.core.signals import request_finished 
from django.dispatch import receiver 
from models import MyModel 
from pipeline import this_takes_forever 


@receiver(post_save, sender=MyModel) 
def my_callback(sender, **kwargs): 
    this_takes_forever(sender) 

La routine this_takes_forever ne IO donc je veux qu'il en remettre à ne pas bloquer la demande trop.

Je pensais que c'était un excellent cas d'utilisation pour le nouveau module asyncio. Mais j'ai du mal à comprendre tout le processus.

Je pense que je devrais être en mesure d'adapter le récepteur de signal comme celui-ci:

@receiver(post_save, sender=MyModel) 
def my_callback(sender, **kwargs): 
    loop = asyncio.get_event_loop() 
    loop.run_until_complete(this_takes_forever(sender)) 
    loop.close() 

fourni this_takes_forever est également adapté pour être un coroutine.

@coroutine 
def this_takes_forever(instance): 
    # do something with instance 
    return instance 

Cela semble trop magique pour fonctionner. Et en fait, il arrête avec un AssertionError:

AssertionError at /new/ 
There is no current event loop in thread 'Thread-1'. 

Je ne vois pas où dois-je commencer la boucle dans ce contexte. Quelqu'un a essayé quelque chose comme ça?

Répondre

3

Vous obtenez pas un avantage dans votre cas:

@receiver(post_save, sender=MyModel) 
def my_callback(sender, **kwargs): 
    this_takes_forever(sender) 

est égal à

@receiver(post_save, sender=MyModel) 
def my_callback(sender, **kwargs): 
    loop = asyncio.get_event_loop() 
    loop.run_until_complete(this_takes_forever(sender)) 
    loop.close() 

en termes de temps d'exécution. loop.run_until_complete attend la fin de this_takes_forever(sender) appel coroutine, de sorte que vous obtenez un appel synchrone dans le deuxième cas ainsi que dans l'ancien. A propos de AssertionError: vous démarrez l'application Django en mode multithread, mais asyncio fait la boucle d'événement par défaut pour le thread principal seulement - vous devez enregistrer une nouvelle boucle pour chaque thread créé par l'utilisateur où vous devez appeler le code asyncio.

Mais, encore une fois, asyncio ne peut pas résoudre votre problème particulier, il est tout simplement incompatible avec Django.

La méthode standard pour Django est de reporter le code de longue durée en tâche de céleri (voir http://www.celeryproject.org/)

+0

Ok avec le premier point. Peut-être que c'est juste un mauvais exemple, je vais essayer de refactoriser pour plus de clarté. Quoi qu'il en soit, je n'utilise pas django dans un mode multithread, en exécutant le 'runserver' par défaut qui est un thread. Regarder plus profond semble que je devrais câbler un serveur non bloquant comme [aiohttp] (https://github.com/KeepSafe/aiohttp) – tutuca

+1

Selon moi, 'runserver' crée en fait un nouveau thread pour exécuter l'application Django à l'intérieur par default - c'est ainsi que fonctionne la fonction '' autoreload''. –

+1

à propos de aiohttp - s'il vous plaît noter que la bibliothèque est trop faible niveau comparant maintenant Django ou, disons, tornado.web. Je travaille sur une interface plus conviviale mais le travail en est à ses débuts. –

Questions connexes