Python(和Flask)中的非消息队列/简单长轮询

34
我正在寻找一种简单的方法来进行长轮询,以便为运行计算并生成图表的小型 Web 界面提供服务。以下是我的 Web 界面需要完成的任务:
  1. 用户在 Web 界面中请求图表/数据
  2. 服务器运行一些计算。
  3. 当服务器正在运行计算时,会通过 AJAX/jQuery 更新一个小容器,以显示计算进度(类似于在控制台中使用 print 语句打印“calculating density function...”)
  4. 计算结束后,向用户展示图表。
由于计算都是在服务器端完成的,因此我不太确定如何轻松地设置这个功能。显然,我需要设置一个 REST API 来处理轮询,在 Flask 中很容易实现。但是,我不确定如何检索实际的更新。一个明显的、虽然对于这个目的来说有点复杂的解决方案是设置一个消息队列并进行长轮询。但是,我不确定这是否是处理这种简单问题的正确方法。
以下是我的问题:
  1. 是否有一种使用文件系统的方法?性能不是很重要。是否可以让 AJAX/jQuery 从文件中查找消息?将进度保存到某个 .json 文件中?
  2. 那 pickle 呢?(我对 pickle 并不是很了解,但也许我可以将消息字典 pickle 化,然后由处理轮询的 API 读取它。)
  3. 轮询是否是正确的方法?有没有更好或更常见的模式来处理这个问题?
我有一种感觉,自己把事情搞得太复杂了,因为我知道这种情况在 Web 上很普遍。经常看到某些东西正在发生,而一个小小的“loading.gif”图像正在运行,同时进行某些计算(例如,在 Google Analytics 中)。
谢谢您的帮助!
2个回答

49

我曾经构建过几个类似的应用,只使用 Flask 和 jQuery 即可。根据那些经验,我可以说你的计划是可行的。

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

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

  3. 长轮询对于你想要实现的目标来说很好。 纯基于 HTTP 的应用有一些限制。WebSockets 和类似的套接字层(如 Socket.IO)“是未来”。但是在我的经验中,找到好的、简单的服务器端实现示例非常困难。我已经努力寻找过。有很多例子想让你设置 Node.js、REDIS 和其他各种中间件。但为什么我们要设置两个或三个独立的中间件服务器呢?这太荒谬了。因此,在 Flask 这样一个简单的、纯 Python Web 框架上进行长轮询是我认为最好的方法。

代码有点多,所以我把一个简化的示例放到了Bitbucket 上的 Mercurial 存储库中,你可以自由地查看、复制或克隆。它有三个部分:

  • serve.py 基于 Python/Flask 的服务器
  • templates/index.html 98% 的 HTML,2% 的模板文件,由基于 Flask 的服务器呈现为 HTML
  • static/lpoll.js 基于 jQuery 的客户端

2
啊,非常感谢您!我今晚会仔细研究您的代码。"但是在我的经验中,找到好的、简单的服务器端实现示例是很困难的。"这正是我所经历的,所以很高兴听到我不是一个人。再次感谢! - aaronlevin
1
如果需要那种级别的同步,那么长轮询基本上是不可能的。可以“同步手表”,并在共同时间戳重新轮询,但由于服务器之间的时间漂移,这比听起来更困难(如果您需要真正精确)。这也引发了竞争/车队条件(除非请求很少/小)。如果您真的需要任何规模的同步,请寻找推送技术,例如WebSockets。其中一个Tornado上面的WS实现,或者非常酷的[Meteor](http://www.meteor.com)。 - Jonathan Eunice
1
@Jonathan 你知道有什么好的资源可以让我了解为什么长轮询是不好的,或者它是如何发展起来的,以及为什么WebSockets是更好的解决方案吗?如果你能指引我一些非标准文档之外的资源,那就太好了。我是一个Web开发新手,现在有些茫然无措。 - viki.omega9
1
@JonathanEunice 看起来链接无法使用 - 你能否在这里或者Github上发布一下?谢谢。 - msanjay
1
@msanjay Bitbucket把仓库拿走了。我会尝试在这个周末把我的仓库迁移到GitHub上。 - Jonathan Eunice
显示剩余5条评论

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

升级到Python 3.7仅是众多优秀原因之一,Quart就是其中之一。


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