【问题标题】:why is gevent-websocket synchronous?为什么 gevent-websocket 是同步的?
【发布时间】:2015-09-20 05:41:05
【问题描述】:

我正在玩 gevent 和 websockets。这是一个简单的回显服务器:

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

和客户:

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

因为gevent 我希望有几个greenlet 异步提供数据;也就是说,如果我多次将一些数据推送到 websocket(快速单击 Push 按钮),我希望在等待 5 秒后同时将其全部返回。

但是,无论我多快点击 Push 按钮,这都是我在控制台中得到的:

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"

为什么它会同步接收我的数据,每 5 秒暂停一次?如何把它变成异步服务器?

【问题讨论】:

    标签: python websocket gevent


    【解决方案1】:

    行为是同步的,因为您自己的代码是同步的。 gevent 只是一个使用事件循环的协程库。它不会神奇地将同步代码变成异步代码。

    请查看文档:http://www.gevent.org/servers.html

    据说服务器每个连接(不是每个请求)产生一个greenlet。因此,对同一连接的多个请求的执行是序列化的。

    如果您想同时处理同一连接的多个请求,则需要生成新的 greenlets,或将处理委托给 greenlets 池。

    这是一个示例(在每个请求中生成一个 greenlet):

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

    注意信号量的存在。因为处理是并发的,所以需要防止两个并发的greenlet同时在socket上写,导致报错。

    最后一点,在这个实现中,不能保证回复会按照请求的顺序发送。

    【讨论】:

    • 猴子用gevent打补丁不是“神奇地将同步代码变成异步代码”吗?
    • 仅当您有多个绿灯和连接时。如果代码正在处理来自单个 greenlet 的单个连接,则 gevent 的协程机制将无济于事。换句话说,只有当你相信它并相应地构建你的代码时,魔法才会发生;-)
    【解决方案2】:

    实际问题是这样的:data = ws.receive()

    这里发生的情况是您的 websocket 现在正在等待单个连接,而整个应用程序只是挂出。

    您有两种解决方案,要么向 ws.receive() 添加超时,要么将其设置为高级应用程序:

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

    例如这里:https://pypi.python.org/pypi/gevent-websocket/

    这将使您的进程完全异步,因此发送和接收不会竞争相同的资源。

    【讨论】:

      猜你喜欢
      • 2012-08-22
      • 2018-12-22
      • 1970-01-01
      • 2019-06-05
      • 1970-01-01
      • 2014-03-01
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      相关资源
      最近更新 更多