如何将Web Sockets与Django WSGI集成

8
我们有一个非常复杂的Django应用程序,目前由apache/mod_wsgi提供服务,并在多个AWS EC2实例上部署,这些实例都在AWS ELB负载均衡器后面。客户端应用程序使用AJAX与服务器进行交互。他们还会定期轮询服务器以检索其状态的通知和更新。我们希望删除轮询并使用Web套接字来替换它,从而实现“推送”。
由于任意实例处理来自客户端的Web套接字请求并保留这些Web套接字,并且因为我们希望将数据推送到可能不在提供推送源数据的相同实例上的客户端,因此我们需要一种方法来将数据路由到适当的实例,然后从该实例路由到适当的客户端Web套接字。
我们意识到apache/mod_wsgi与Web套接字不兼容,并计划使用nginx/gunicorn替换这些组件,并使用gevent-websocket worker。但是,如果其中一个或多个工作进程收到来自客户端建立Web套接字的请求,并且如果工作进程的生命周期由主gunicorn进程控制,则不清楚其他工作进程或实际上非gunicorn进程如何向这些Web套接字发送数据。
一个具体的案例是:发出HTTP请求的用户被引导到一个EC2实例(主机),并且期望的行为是将数据发送到在完全不同实例中打开Web套接字的另一个用户。人们可以轻松地想象一个系统,其中在每个实例上运行的消息代理(例如rabbitmq)可以发送包含要通过Web套接字发送到连接到该实例的客户端的数据的消息。但是,如何处理这些消息的处理程序访问在gunicorn的工作进程中接收到的Web套接字呢?由gevent-websocket创建的高级Python Web套接字对象并提供给工作进程无法进行pickling(它们是没有支持pickling的实例方法),因此它们不能轻松地被共享到某个长时间运行的外部进程中。
实际上,这个问题的根源在于Web套接字和基于WSGI的HTTP请求处理程序如何在我描述的环境中相互协作?似乎不“正确”,即gunicorn工作进程旨在处理HTTP请求,会生成长时间运行的线程来保持Web套接字,并支持从其他进程处理消息以发送消息到已通过这些工作进程附加的Web套接字。
有人能解释一下在我描述的环境中,Web套接字和基于WSGI的HTTP请求处理程序可能如何相互协作吗?
谢谢。
2个回答

1

我认为你的判断是正确的,mod_wsgi和websockets组合起来是一件麻烦的事情。

你会发现所有的wsgi工作进程都被web sockets占用了,试图(大幅度)增加worker池的大小可能会因为内存使用和上下文切换而使服务器崩溃。

如果你想坚持同步的wsgi worker架构(与gevent、twisted、tornado等实现的反应式方法相对),我建议你看看uWSGI作为一个应用服务器。最近的版本可以以旧的方式处理一些URL(即您现有的django视图仍然像以前一样工作),并将其他url路由到异步websocket处理程序。这可能是一个相对平滑的迁移路径。


我们已经转换到一种模式,其中我们使用NGINX作为我们的HTTP代理,并将其代理到gunicorn(django)或nodejs。 Nodejs应用程序处理我们的Web套接字。 我们可能很快会切换到uWSGI,并尝试它,因为我们认为性能会更好。 是否将Web套接字支持切换回Python / Django取决于我们是否真的认为这样做有很大优势。 现在,Nodejs似乎可以很好地处理这些WebSocket连接。 - eswenson

0
似乎不太对,gunicorn的工作进程用来处理HTTP请求,但它们会生成长时间运行的线程,以便挂住Web套接字并支持从其他进程处理消息,并发送消息到那些通过这些工作进程附加的Web套接字。
为什么不呢?毕竟这是长时间运行的连接。有一条长时间运行的线程来处理这样的连接对我来说似乎非常自然。
在这些事件驱动的情况下,通常将写入与读取分开处理。
当前正在处理Web套接字连接的工作程序将等待相关消息从消息服务器传递下来,然后将其传递到Web套接字。
如果您愿意,也可以使用gevent的异步友好队列来处理代码内的消息传递。

如果服务器在处理一个HTTP请求时需要向已连接到同一主机或不同主机上的另一个工作进程的另一个用户发送推送通知,则发送方工作进程必须将消息排队(可能是到每个工作进程都订阅的AMQP服务器),而接收方工作进程(持有来自目标客户端的websocket)则必须从队列中读取消息并将其发送到websocket。这个架构看起来合理吗? - eswenson
我之前对于使用工作进程来进行除请求/响应处理以外的“其他工作”感到不太舒服,但是你似乎在说它们也可以用于其他用途。是这样吗? - eswenson
一个WebSocket不再只是简单的请求/响应处理了,对吧?现在它是一个完整的双工通道。如果你愿意,你可以将WebSocket连接交给一个单独的工作池来处理。是的,你提出的建议对我来说似乎是一个正常的架构。 :) - Ivo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接