why is gevent-websocket synchronous?

2020-02-23 19:55发布

I am playing 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 the 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>

Because of gevent I was expecting to have several greenlets serving the data asynchronously; that is, if I pushed some data to the websocket several times (quickly clicking on the Push button), I was expecting to have it all back simultaneously after 5 seconds of waiting.

However, no matter how fast I click the Push 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, pausing every 5 seconds? How do I turn it into an asynchronous server?

2条回答
家丑人穷心不美
2楼-- · 2020-02-23 20:37

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

What is happening here is your websocket is now waiting for a SINGLE connection while the whole app just hangs out.

You have two solutions, either add a timeout to ws.receive() OR set it up 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 exampled here: https://pypi.python.org/pypi/gevent-websocket/

This would then setup your process totally async, thus sending and receiving wouldn't compete for the same resource.

查看更多
不美不萌又怎样
3楼-- · 2020-02-23 20:46

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

Please have a look at the documentation at: http://www.gevent.org/servers.html

It is said that the servers spawn one greenlet per connection (not per request). The execution of multiple requests for the same connection is therefore serialized.

If you want to concurrently handle multiple requests for the same connection, you need to spawn new greenlets, or delegate the processing to a pool of greenlets.

Here is an example (spawning a greenlet at 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()

Note the presence of the semaphore. Because the processing is concurrent, it is needed to prevent two concurrent greenlets to write at the same time on the socket, resulting in corrupted messages.

Last point, with this implementation, there is no guarantee that the replies will be sent in the order of the requests.

查看更多
登录 后发表回答