2010-10-06 10 views
6

por lo que tienen un controlador a continuación:Python Tornado - hacer vuelta de correo de inmediato mientras que la función asíncrona sigue trabajando

class PublishHandler(BaseHandler): 

    def post(self): 
     message = self.get_argument("message") 
     some_function(message) 
     self.write("success") 

El problema que estoy enfrentando es que some_function() toma un poco de tiempo para ejecutar y me gustaría la solicitud posterior para volver inmediatamente cuando se llama y para que some_function() se ejecute en otro subproceso/proceso si es posible.

Estoy usando berkeley db como la base de datos y lo que estoy tratando de hacer es relativamente simple.

Tengo una base de datos de usuarios, cada uno con un filtro. Si el filtro coincide con el mensaje, el servidor enviará el mensaje al usuario. Actualmente estoy probando con miles de usuarios y, por lo tanto, en cada publicación de un mensaje a través de una solicitud de publicación, se está iterando a través de miles de usuarios para encontrar una coincidencia. Esta es mi implementación ingenua de hacer cosas y, por lo tanto, mi pregunta. ¿Cómo hago esto mejor?

Respuesta

7

Usted puede ser capaz de lograr esto mediante el uso de sus IOLoop 's add_callback método de este modo:

loop.add_callback(lambda: some_function(message)) 

Tornado ejecutará la devolución de llamada en la próxima IOLoop sucedido, y que puede (que tendría que profundizar en las agallas de Tornado para saber con certeza, o alternativamente probarlo) permitir que la solicitud se complete antes de que se ejecute el código.

El inconveniente es que ese código de ejecución prolongada que ha escrito aún tardará en ejecutarse, y esto puede terminar bloqueando otra solicitud. Eso no es ideal si tiene muchas de estas solicitudes a la vez.

La solución más infalible es ejecutarla en una secuencia o proceso separado. La mejor forma de con Python es usar un proceso, debido al GIL (recomiendo leer sobre eso si no está familiarizado con él). Sin embargo, en una máquina con un solo procesador, la implementación con hilos funcionará igual de bien y puede ser más fácil de implementar.

Si va por la ruta enhebrada, puede construir un buen módulo "async executor" con un mutex, un hilo y una cola. Consulte el módulo multiprocessing si desea utilizar el proceso por separado.

1

He intentado esto, y creo que la solicitud no se completa antes de que se llamen las devoluciones de llamada.

creo que un truco sucio sería llamar a dos niveles de add_callback, por ejemplo .:

def get(self): 
    ... 
    def _defered(): 
     ioloop.add_callback(<whatever you want>) 
    ioloop.add_callback(_defered) 
    ... 

pero estos son en el mejor de los cortes. Estoy buscando una mejor solución en este momento, probablemente termine con una cola de mensajes o una solución de subproceso simple.

Cuestiones relacionadas