10

我想实现一个(开源)Web 应用程序,用户通过他的浏览器向 Python Web 应用程序发送某种请求。请求数据用于定义和提交某种繁重的计算工作。计算工作外包给“工人后端”(也是 Python)。在作业处理期间,作业会随着时间的推移经历不同的阶段(理想情况下,从“提交”到中间状态到“完成”)。我想要完成的是实时向用户显示当前的工作状态。这意味着工作人员后端必须将作业状态传回 Web 应用程序。然后,Web 应用程序必须将信息推送到用户的浏览器。我为您带来了一张示意性描述基本思想的图片: 示意图问题描述

红色圆圈中的数字表示事件的时间顺序。“web app”和“worker backend”仍有待设计。现在,如果您能帮助我做出一些技术决策,我将不胜感激。

我的问题,特别是:

  1. 我应该在 Web 应用程序和工作人员后端之间应用哪种消息传递技术? 当工作人员后端发出关于某个工作的信号(某种消息)时,它必须触发 Web 应用程序中的某个事件。因此,我需要某种与最初请求提交作业的客户端相关联的回调。我想我需要一些发布/订阅机制,工作后端发布和网络应用订阅。当 Web 应用程序收到消息时,它会通过向客户端发送状态更新来对其做出反应。我希望工作人员后端具有可扩展性,并且与 Web 应用程序紧密分离。因此,我正在考虑使用 Redis 或 ZeroMQ 来完成这项任务。你怎么看?我的整个方法是不是有点太复杂了?

  2. 我应该使用哪种技术将信息推送到浏览器? 只是出于完美主义,我想要实时更新。我不想以高频率轮询。当工作人员后端发出消息时,我希望立即推送到客户端:-)。另外,我不需要最大的浏览器支持。这个项目首先对我来说或多或少是一个技术演示。我应该去 HTML5 服务器发送的事件/websockets 吗?或者你会推荐其他方式吗?

非常感谢您提前提出的建议。

4

3 回答 3

4

一个选项是使用 WebSocket。如果您走这条路,您可能会查看Autobahn,其中包括适用于 Python (Twisted) 的客户端和服务器,以及基于 WebSocket 的 RPC+PubSub 协议(带有适用于 Python、JavaScript 和 Android 的库)。使用 RPC+PubSub 订阅可以保护大量工作,并且可能满足您的需求(工作提交 => RPC,工作工作更新 => PubSub)。

AutobahnPython 在 Twisted 上运行,它还可以充当 WSGI 容器,从而可以运行 Flask(或其他基于 WSGI 的 Web 框架)。您可以在 1 个端口/服务器上运行所有内容。后者在 GitHub Autobahn 存储库中有一个示例。

免责声明:我是 Autobahn 和WAMP的原作者,并为 Tavendo 工作。

详细信息: 我假设您的工作人员执行 CPU 密集型和/或阻塞操作。

首先,您的员工是纯 Python 还是外部程序?

如果是后者,您可以使用 Twisted 进程协议实例,该实例通过 stdio 管道(以非阻塞方式)从主 Twisted 线程进行通信。如果是前者,您可以使用 Twisted 后台线程池并使用 Twisted deferToThread(参见:http ://twistedmatrix.com/documents/current/core/howto/threading.html )。

Autobahn 在主 Twisted reactor 线程上运行。如果您的工作人员也这样做(参见之前的评论),那么您可以直接调用 WebSocket/WAMP 工厂/协议实例上的方法。如果不是(worker 在后台线程上运行),您应该通过 callFromThread 调用这些方法。

如果您使用 WAMP,主要是为每个工作人员获取 WampServerFactory 的引用。然后,worker 可以通过调用适当的工厂方法向所有订阅者发送 PubSub 事件。

于 2012-10-04T20:59:25.607 回答
3

既然您在谈论 Python Web 应用程序,我建议您查看:

我应该在 Web 应用程序和工作人员后端之间应用哪种消息传递技术?

Celery - 将您的工作分解为较小的任务,这些任务返回需要显示给客户的结果

我应该使用哪种技术将信息推送到浏览器?

NodeJS上的Socket IO类型的服务器端 JS 框架或用于您的 Python Web 框架的 Web 套接字库

如果你不是太依赖 python,看看Meteor

基于此线程,实时更新从服务器到 Web 客户端的进度的其他方法可能包括将进度状态写入 redis 数据库或使用基于celery 子任务的异步结果的STOMP 协议使用Oribited / Morbid(均基于Twisted

于 2012-10-04T19:13:17.150 回答
3

为了有任何用途,您的 Web 应用程序将有一个数据库。我会在该数据库中创建一个专门用于这些作业的表。每个工作都有一个“状态”。

这简化了您的系统,因为您可以发送开始工作的请求并将其交给后端工作人员(zmq 是这个 IMO 的一个很好的解决方案)。由于您使用 python 作为后端,因此让您的工作人员作业更新其在数据库中的当前工作作业或拥有另一个“更新程序”,其唯一工作是更新数据库中的字段(保持逻辑分开)是非常简单的将提供更好的解决方案,如果您正在进行大量更新,则允许您启动多个“更新程序”)

然后对于您的前端,由于您不想轮询服务器,我会做一些“长轮询”。您实际上所做的是轮询服务器,但服务器实际上永远不会“响应”,直到您感兴趣的数据发生变化。一旦发生变化,您就会响应请求。在前端,您的 JS 在收到最新更新后立即重新建立连接。只要您使用也是跨浏览器的 JS 框架(我建议使用 jQuery),该解决方案就可以跨浏览器兼容。


要消除 Web 应用程序数据库轮询,请执行以下操作:

将初始请求作为对 Web 应用程序的长轮询请求,Web 应用程序向您的后端发送一条 zmq 消息(可能需要使用 REQ/REP 套接字完成)并等待。它一直等到它从 zmq 后端收到状态更改的消息。当它获得状态更改时,它会以更改响应前端。此时,前端将发送一个新的长轮询请求(此作业的当前 id 可以是其身份),Web 应用程序将重新连接到后端并等待另一个状态更改。使这项工作的诀窍是使用 ZMQZMQ_IDENTITY最初创建时的套接字(在第一个请求中)。这将允许 Web 应用程序重新连接到相同的后端套接字并获得新的更新。当后端有新的更新要发送时,它会向 Web 应用程序发出信号,然后 Web 应用程序将响应长轮询请求及其状态更改。这样就polling没有后端数据库,一切都是后端工作人员的事件驱动。

我会设置某种看门狗,如果前端消失(切换页面或关闭浏览器),后端套接字将正确关闭。当它改变状态时,他们不需要坐在那里无限期地阻塞。

于 2012-10-05T12:19:19.683 回答