32

我正在寻找一种简单的(即,不需要我设置单独的服务器来处理消息队列)方法来对运行计算并生成图形的小型 Web 界面进行长轮询。这是我的网络界面需要做的:

  1. 用户在 Web 界面中请求图形/数据
  2. 服务器运行一些计算。
  3. 当服务器运行计算时,一个小容器会随着计算进度更新(可能通过 AJAX/jQuery)(类似于您在带有 print 的控制台中所做的事情(即 print 'calculating density function...'))
  4. 计算完成并向用户显示图表。

由于计算都是在服务器端完成的,我不确定如何轻松设置它。显然,我想设置一个 REST API 来处理轮询,这在 Flask 中很容易。但是,我不确定如何检索实际更新。为此目的,一个明显的,虽然复杂的解决方案是设置一个消息队列并进行一些长轮询。但是,我不确定这对于这么简单的事情是正确的方法。

以下是我的问题:

  1. 有没有办法使用文件系统来做到这一点?性能不是一个大问题。AJAX/jQuery 可以从文件中查找消息吗?将进度保存到某个 .json 文件?
  2. 腌制呢?(我对酸洗不太了解,但也许我可以酸洗一个消息字典,它可以被处理轮询的 API 读取)。
  3. 民意调查甚至是正确的方法吗?有没有更好或更常见的模式来处理这个问题?

我觉得我把事情复杂化了,因为我知道这种事情在网络上很常见。我经常看到一些事情正在发生,并且在进行一些计算(例如,在 Google Analytics 中)时,正在运行一个小的“loading.gif”图像。

谢谢你的帮助!

4

2 回答 2

47

我已经使用 Flask 和 jQuery 构建了几个这样的应用程序。根据那次经验,我会说你的计划是好的。

  1. 不要使用文件系统。您将遇到 JavaScript 安全问题/保护。万一您找到合理的解决方法,您仍然没有任何可移植或可扩展的东西。相反,请使用小型本地 Web 服务框架,例如 Flask。

  2. 不要腌制。使用 JSON。它是 Web 应用程序和 REST 接口的语言。jQuery 和那些用于绘制图表、图形等的漂亮的基于 jQuery 的插件将需要 JSON。它易于使用,可读性强,对于小型应用程序,没有理由去其他地方。

  3. 长轮询对于您想要完成的事情是很好的。纯基于 HTTP 的应用程序有一些限制。WebSockets 和类似的套接字层,如 Socket.IO,“是未来”。但是,根据我的经验,要找到服务器端实现的好的、简单的例子是很困难的。我仔细看了 有很多示例希望您设置 Node.js、REDIS 和其他中间件。但是我们为什么要设置两三个独立的中间件服务器呢?这很可笑。因此,对像 Flask 这样的简单纯 Python Web 框架进行长时间轮询是 IMO 的出路。

该代码不仅仅是一个片段,因此我没有将其包含在此处,而是将一个简化的示例放入bitbucket 上的 Mercurial 存储库中,您可以自由查看、复制或克隆。分为三个部分:

  • serve.py基于 Python/Flask 的服务器
  • templates/index.html98% HTML,2% 模板文件,基于 Flask 的服务器将呈现为 HTML
  • static/lpoll.js基于 jQuery 的客户端
于 2012-06-22T18:13:30.450 回答
14

在大多数浏览器对 Web Sockets 的简单、自然的支持出现之前,并且在它与 Flask 应用程序轻松集成之前,长轮询是一种合理的解决方法。但是在 2013 年中期,Web Socket 支持已经取得了长足的进步。

这是一个示例,与上面的示例类似,但集成了 Flask 和 Web Sockets。它运行在geventgevent-websocket 的服务器组件之上。

请注意,此示例并非旨在成为 Web Socket 杰作。它保留了很多lpoll结构,使它们更容易比较。但它立即提高了 Web 应用程序的响应能力、服务器开销和交互性。

Python 3.7+ 的更新

自从最初的答案 5 年后,WebSocket 变得更容易实现。从 Python 3.7 开始,异步操作已经成熟为主流用途。Python Web 应用程序是完美的用例。他们现在可以像 JavaScript 和 Node.js 一样使用异步,而留下一些“并行”的怪癖和复杂性。特别是,请查看Quart。它保留了 Flask 的 API 并与许多 Flask 扩展兼容,但支持异步。一个关键的副作用是 WebSocket 连接可以与 HTTP 连接并行处理。例如:

from quart import Quart, websocket

app = Quart(__name__)

@app.route('/')
async def hello():
    return 'hello'

@app.websocket('/ws')
async def ws():
    while True:
        await websocket.send('hello')

app.run()

Quart 只是升级到 Python 3.7 的众多重要原因之一。

于 2013-08-07T01:36:27.493 回答