¿Por qué es gevent-websocket síncrono?

Estoy jugando con gevent y websockets. Este es un servidor de eco simple:

from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler from gevent import sleep from datetime import datetime def app(environ, start_response): ws = environ['wsgi.websocket'] while True: data = ws.receive() print('{} got data "{}"'.format( datetime.now().strftime('%H:%M:%S'), data)) sleep(5) ws.send(data) server = WSGIServer(("", 10004), app, handler_class=WebSocketHandler) server.serve_forever() 

y el cliente:

       var ws = new WebSocket("ws://localhost:10004"); ws.onmessage = function(evt) { console.log(evt) }; $('#push_data').click(function(){ console.log('sending data...'); ws.send('sample data'); });   

Debido a gevent , esperaba tener varios greenlets al servicio de los datos de forma asíncrona; es decir, si presioné algunos datos en el websocket varias veces (haciendo clic rápidamente en el botón Push), esperaba recuperarlos todos simultáneamente luego de 5 segundos de espera.

Sin embargo, no importa lo rápido que haga clic en el botón Push, esto es lo que obtengo en la consola:

 18:28:07 got data "sample data" 18:28:12 got data "sample data" 18:28:17 got data "sample data" 18:28:22 got data "sample data" 18:28:27 got data "sample data" 

¿Por qué recibe mis datos de forma síncrona, haciendo una pausa cada 5 segundos? ¿Cómo lo convierto en un servidor asíncrono?

El comportamiento es síncrono porque su propio código es síncrono. gevent es solo una biblioteca de coroutine que usa un bucle de eventos. No convierte mágicamente el código síncrono en código asíncrono.

Por favor, eche un vistazo a la documentación en: http://www.gevent.org/servers.html

Se dice que los servidores generan un greenlet por conexión (no por solicitud). La ejecución de múltiples solicitudes para la misma conexión es por lo tanto serializada.

Si desea manejar simultáneamente múltiples solicitudes para la misma conexión, necesita generar nuevos greenlets o delegar el procesamiento a un grupo de greenlets.

Aquí hay un ejemplo (generando un greenlet en cada solicitud):

 import gevent from gevent.pywsgi import WSGIServer from gevent.lock import Semaphore from geventwebsocket.handler import WebSocketHandler from datetime import datetime def process(ws,data,sem): print('{} got data "{}"'.format(datetime.now().strftime('%H:%M:%S'), data)) gevent.sleep(5) with sem: ws.send(data) def app(environ, start_response): ws = environ['wsgi.websocket'] sem = Semaphore() while True: data = ws.receive() gevent.spawn(process,ws,data,sem) server = WSGIServer(("", 10004), app,handler_class=WebSocketHandler) server.serve_forever() 

Nótese la presencia del semáforo. Debido a que el procesamiento es simultáneo, es necesario evitar que dos greenlets simultáneos escriban al mismo tiempo en el socket, lo que da como resultado mensajes dañados.

Último punto, con esta implementación, no hay garantía de que las respuestas se enviarán en el orden de las solicitudes.

El problema real es este: datos = ws.receive ()

Lo que está sucediendo aquí es que su websocket ahora está esperando una conexión SINGLE mientras la aplicación completa simplemente se cuelga.

Tiene dos soluciones, ya sea agregar un tiempo de espera a ws.receive () O configurarlo como una aplicación de alto nivel:

 from geventwebsocket import WebSocketServer, WebSocketApplication, Resource class EchoApplication(WebSocketApplication): def on_open(self): print "Connection opened" def on_message(self, message): self.ws.send(message) def on_close(self, reason): print reason WebSocketServer(('', 8000), Resource({'/': EchoApplication}).serve_forever() 

como se muestra aquí: https://pypi.python.org/pypi/gevent-websocket/

Esto configuraría su proceso de forma totalmente asíncrona, por lo que el envío y la recepción no competirían por el mismo recurso.