2014-05-21 12 views
6

mio elaborazione modello Post utilizza il segnale post_save:segnali asincroni con 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) 

Il this_takes_forever di routine fa IO quindi voglio rinviare per evitare il blocco della richiesta troppo.

Ho pensato che fosse un ottimo caso per il nuovo modulo asyncio. Ma ho difficoltà a prendere in considerazione l'intero processo.

Credo dovrei essere in grado di adattare il ricevitore di segnale in questo modo:

@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() 

fornito this_takes_forever è inoltre atto ad essere un coroutine.

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

Questo suona troppo magico per funzionare. Ed in effetti ferma con una AssertionError:

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

Non vedo dove devo iniziare il ciclo in questo contesto. Qualcuno ha provato qualcosa del genere?

risposta

3

non ottenete alcun beneficio nel vostro caso:

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

è uguale a

@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() 

in termini di tempo di esecuzione. loop.run_until_complete attende la fine della chiamata di coroutine this_takes_forever(sender), in modo da ottenere la chiamata sincrona nel secondo caso e nella precedente.

Circa AssertionError: si inizia Django app in modalità multithread, ma asyncio rende ciclo di eventi di default solo il thread principale - si dovrebbe registrare nuovo loop per ogni thread creato dall'utente quando è necessario chiamare asyncio codice.

Ma, ripeto, asyncio non può risolvere il tuo particolare problema, è solo incompatibile con Django.

Il metodo standard per Django è di rinviare il codice di lungo corso nel compito di sedano (vedi http://www.celeryproject.org/)

+0

Ok con il primo punto. Forse questo è solo un cattivo esempio, proverò a fare il refactoring per maggiore chiarezza. Ad ogni modo non sto usando django in nessuna modalità multithread, semplicemente eseguendo il 'runserver' predefinito che è un thread. Guardando più a fondo sembra che avrei bisogno di collegare alcuni server non bloccanti come [aiohttp] (https://github.com/KeepSafe/aiohttp) – tutuca

+1

Dalla mia comprensione 'runserver' crea in realtà un nuovo thread per eseguire l'applicazione Django al suo interno da default - è così che funziona la funzione "autoreload'. –

+1

su aiohttp - per favore nota che la libreria è troppo bassa ora paragonando Django o, ad esempio, tornado.web. Sto lavorando su un'interfaccia più user friendly ma il lavoro è in una fase molto precoce. –