programming mac example async python asynchronous tornado

python - mac - ¿Cómo funciona Long-Polling en Tornado?



tornado python mac (2)

En la demostración de chat de Tornado, tiene un método como este:

@tornado.web.asynchronous def post(self): cursor = self.get_argument("cursor", None) global_message_buffer.wait_for_messages(self.on_new_messages, cursor=cursor)

Soy bastante nuevo en esta larga encuesta, y realmente no entiendo exactamente cómo funciona el threading, aunque dice:

Mediante el uso de E / S de red sin bloqueo, Tornado puede escalar a decenas de miles de conexiones abiertas ...

Mi teoría era que al hacer una aplicación simple:

import tornado.ioloop import tornado.web import time class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): print("Start request") time.sleep(4) print("Okay done now") self.write("Howdy howdy howdy") self.finish() application = tornado.web.Application([ (r''/'', MainHandler), ])

Que si hice dos solicitudes seguidas (es decir, abrí dos ventanas del navegador y las actualicé rápidamente), vería esto:

Start request Start request Okay done now Okay done now

En cambio, veo

Start request Okay done now Start request Okay done now

Lo que me lleva a creer que, de hecho, está bloqueando en este caso. ¿Por qué mi código está bloqueando y cómo consigo un código para hacer lo que espero? Obtengo el mismo resultado en Windows 7 con un Core i7 y un linux Mint 13 con dos núcleos.

Editar:

Encontré un método: si alguien puede proporcionar un método que funciona en varias plataformas (no estoy demasiado preocupado por el rendimiento, solo que no bloquea), aceptaré esa respuesta.


El problema con el código en la pregunta original es que cuando llamas a time.sleep(4) estás bloqueando efectivamente la ejecución del bucle de eventos durante 4 segundos. Y la respuesta aceptada tampoco resuelve el problema (en mi humilde opinión).

El servicio asincrónico en Tornado funciona con confianza. Tornado llamará a sus funciones cada vez que ocurra algo, pero confía en que le devolverá el control tan pronto como sea posible. Si bloqueas con time.sleep() entonces se time.sleep() esta confianza: Tornado no puede manejar nuevas conexiones.

Usar múltiples hilos solo oculta el error; ejecutar Tornado con miles de subprocesos (para que pueda atender miles de conexiones simultáneamente) sería muy ineficiente. La forma adecuada es ejecutar un único hilo que solo se bloquea dentro de Tornado (en select o en cualquier forma de Tornado para escuchar los eventos) - no en su código (para ser exactos: nunca en su código).

La solución adecuada es simplemente regresar de get(self) justo antes de time.sleep() (sin llamar a self.finish() ), como este:

class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): print("Starting")

Por supuesto, debe recordar que esta solicitud todavía está abierta y llame a write() y finish() sobre ella más tarde.

Te sugiero que eches un vistazo a la demostración de chat . Una vez que eliminas la autenticación, obtienes un muy buen ejemplo del servidor de sondeo largo asincrónico.


La forma correcta de convertir su aplicación de prueba en un formulario que no bloqueará el IOLoop es así:

from tornado.ioloop import IOLoop import tornado.web from tornado import gen import time @gen.coroutine def async_sleep(timeout): """ Sleep without blocking the IOLoop. """ yield gen.Task(IOLoop.instance().add_timeout, time.time() + timeout) class MainHandler(tornado.web.RequestHandler): @gen.coroutine def get(self): print("Start request") yield async_sleep(4) print("Okay done now") self.write("Howdy howdy howdy") self.finish() if __name__ == "__main__": application = tornado.web.Application([ (r''/'', MainHandler), ]) application.listen(8888) IOLoop.instance().start()

La diferencia está reemplazando la llamada al tiempo. time.sleep con uno que no bloqueará el IOLoop. Tornado está diseñado para gestionar muchas E / S simultáneas sin necesidad de múltiples subprocesos o subprocesos, pero seguirá bloqueándose si utiliza API síncronas. Para que su solución de larga duración maneje la concurrencia de la manera que le gustaría, debe asegurarse de que no se bloqueen las llamadas de larga ejecución.