Why is gevent-websocket in sync?

I play with gevent and websockets. This is a simple echo server:

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

and client:

 <html> <body> <button type="button" id="push_data">Push</button> </body> <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.js"></script> <script> 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'); }); </script> </html> 

Due to gevent I expected several potions to serve data asynchronously; that is, if I pushed some data into the websocket several times (I quickly press the Push button), I expected that all of them would be returned at the same time after 5 seconds of waiting.

However, no matter how fast I click the start button, this is what I get in the console:

 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" 

why does it receive my data synchronously, pause every 5 seconds? How to turn it into an asynchronous server?

+5
source share
2 answers

The behavior is synchronous because your own code is synchronous. gevent is just a coroutine library using an event loop. This does not magically turn synchronous code into asynchronous code.

Please see the documentation at: http://www.gevent.org/servers.html

They say that on the servers one green appears for each connection (not for each request). Therefore, the execution of multiple queries for the same connection is serialized.

If you want to process several requests at the same time for the same connection, you need to create new green fields or delegate processing to the green pool.

Here is an example (generating a potion for each request):

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

Pay attention to the presence of a semaphore. Since processing is parallel, it is necessary to prevent the simultaneous recording of two parallel skeletons on the socket, which leads to message corruption.

The latter, with this implementation, does not guarantee that responses will be sent in the order of requests.

+6
source

The actual problem is this: data = ws.receive ()

What happens here, your websocket is now waiting for a SINGLE connection, while the entire application just hangs.

You have two solutions: add a timeout to ws.receive () or configure it as a high-level application:

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

as shown here: https://pypi.python.org/pypi/gevent-websocket/

Then it will fully configure the asynchronous process, so sending and receiving will not compete for the same resource.

0
source

All Articles