uWSGI:通过 websockets 与多个客户端一起工作

Posted

技术标签:

【中文标题】uWSGI:通过 websockets 与多个客户端一起工作【英文标题】:uWSGI: working with multiple clients through websockets 【发布时间】:2015-05-23 17:04:02 【问题描述】:

我是 uWSGI 的新手,我正在开发一个需要大量 web-socket 通信的 web 应用程序。我决定通过创建一个简单的聊天应用程序来练习。

来自uWSGI docs:

def application(env, start_response):
    # complete the handshake
    uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'], env.get('HTTP_ORIGIN', ''))
    while True:
        msg = uwsgi.websocket_recv()
        uwsgi.websocket_send(msg)

我不想使用这种方法,因为

    只有在客户向您发送内容后,您才能向他发送消息; 您无权访问其他 websocket 连接,因此基本上您只能与 1 个客户端通信。

但是,同一页面有一个example 说明如何实现聊天。这会很棒,但他们在示例中使用了 Redis:

    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    channel = r.pubsub()
    channel.subscribe('foobar')

    websocket_fd = uwsgi.connection_fd()
    redis_fd = channel.connection._sock.fileno()

    while True:
        uwsgi.wait_fd_read(websocket_fd, 3)
        uwsgi.wait_fd_read(redis_fd)
        uwsgi.suspend()

据我所知,这里的 Redis 被用作外部服务器,允许不同的 uWSGI 请求处理程序使用相同的数据。

这真的需要那么难吗?

看看使用 Node.js 的聊天解决方案(示例取自 javascript.ru):

var WebSocketServer = new require('ws');

// connected clients
var clients = ;

// WebSocket-server serves 8081 port
var webSocketServer = new WebSocketServer.Server(
  port: 8081
);
webSocketServer.on('connection', function(ws) 

  var id = Math.random();
  clients[id] = ws;
  console.log("new connection " + id);

  ws.on('message', function(message) 
    console.log("recieved a new message: " + message);

    for (var key in clients) 
      clients[key].send(message);
    
  );

  ws.on('close', function() 
    console.log("connection closed " + id);
    delete clients[id];
  );

);

我真正喜欢这里的是

    基于事件的方法让我可以随时向客户发送数据 所有客户端都存储在一个简单的clients 字典中。我可以轻松访问它,无需使用某些外部服务器在客户端之间交换数据。

为了解决 uWSGI 中的第一个问题(基于事件的非阻塞方法),我编写了以下代码 sn-p:

import uwsgi
from threading import Thread


class WebSocket(Thread):
    def __init__(self, env):
        super().__init__()
        self.listeners = []
        self._env = env

    def run(self):
        self._working = True
        uwsgi.websocket_handshake(
            self._env['HTTP_SEC_WEBSOCKET_KEY'], self._env.get('HTTP_ORIGIN', ''))

        while self._working:
            msg = uwsgi.websocket_recv()
            for listener in self.listeners:
                listener(msg)

    def send(self, msg):
        uwsgi.websocket_send(msg)

    def close(self):
        self._working = False

所以我的第一个问题是这是否可行。

第二个问题是我如何在请求处理程序之间交换数据。我觉得我完全误解了 uWSGI 设计。 我使用uwsgi --http :80 --wsgi-file=main.py --master --static-map /st=web-static 来测试我的应用程序。理想情况下,我会在main.py 中定义一个对象并使用它,但我假设这个main.py 将在不同的工作人员/线程/进程中多次初始化。

我已经看到过关于数据交换的类似问题:Communication between workers in uwsgi

答案是

注意,只有当你有一个工作人员/进程时,它才会起作用。另一种常见的方法是使用 uWSGI 缓存框架(名称具有误导性,实际上是共享字典)。它将允许您在工作人员和线程之间共享数据。

我将此 uWSGI 缓存框架视为某种独立的外部数据存储(参见上面的 Redis 示例)。但是在我看到 Node.js 上的简洁实现之后,我不想使用任何缓存框架,而只想在所有请求处理程序之间共享相同的 python 对象。

【问题讨论】:

【参考方案1】:

首先,您应该投资于学习同步与异步编程范式。节点方法似乎更容易,但这只是因为您需要管理一个进程。如果您需要扩展(到多台机器或简单的多进程),您将回到“python 问题”。拥有外部通道(如 redis)是一种常见模式,您应该使用它,因为它可以让您轻松扩展。

关于 python、uWSGI 和 websockets,我强烈建议你看看 gevent。 uWSGI websockets 系统支持它,并且有很多例子。您将能够增加并发性,而无需依赖基于回调的编程。

最终(但前提是你喜欢回调)你可以看看龙卷风。

【讨论】:

以上是关于uWSGI:通过 websockets 与多个客户端一起工作的主要内容,如果未能解决你的问题,请参考以下文章

带有 uWSGI 本机异步 websockets 和 redis 的错误文件描述符

django + nginx + uwsgi + websocket

django 多线程 + uWSGI 多线程 遇到的坑

通过 websocket 继续向客户端发送数据

springboot整合websocket实现客户端与服务端通信

如何配置 uwsgi + nginx + Django + websocket?