长轮询/HTTP 流一般问题

Posted

技术标签:

【中文标题】长轮询/HTTP 流一般问题【英文标题】:Long Polling/HTTP Streaming General Questions 【发布时间】:2011-11-05 00:38:28 【问题描述】:

我正在尝试使用php 和jquery 制作一个理论上的网络聊天应用程序,我已经阅读了有关长轮询和http 流的信息,并且我设法应用了文章中介绍的大多数原则。但是,有两件事我仍然无法理解。

长轮询

服务器如何知道何时发送了更新?是否需要不断查询数据库或有更好的方法?

使用 HTTP 流

如何在 Ajax 连接仍处于活动状态时检查结果?我知道用于 ajax 调用的 jQuery 的 success 函数,但是我如何在同时连接仍在进行中检查数据?

我将不胜感激任何和所有的答案,在此先感谢。

【问题讨论】:

我的大学在一个使用长轮询请求(几个小时)的项目上工作。在尝试了几种技术后,他们最终使用了轨道。我不知道细节,只知道应用程序现在非常稳定并且他们很满意。 除了我使用 PHP,虽然我也很想了解它。 他们决定使用 Orbited 作为中间服务。应用程序的主要部分是 PHP,带有一个 javascripthtml 等)前端。 【参考方案1】:

是的,彗星式的技术通常在一开始就让大脑爆炸——只是让你以不同的方式思考。另一个问题是 PHP 可用的资源不多,因为每个人都在用 node.js、Python、Java 等做 Comet。

我会尝试回答你的问题,希望它可以为人们提供一些关于这个主题的信息。

服务器如何知道更新何时发送?是否需要不断查询数据库或有更好的方法?

答案是:在最一般的情况下,您应该使用消息队列 (MQ)。 RabbitMQ 或 Redis 存储中内置的 Pub/Sub 功能可能是一个不错的选择,尽管市场上有许多竞争解决方案,例如 ZeroMQ、Beanstalkd 等。

因此,您可以订阅 MQ 事件,而不是持续查询您的数据库,然后挂起,直到其他人发布您订阅的消息,MQ 将叫醒你并发送消息。聊天应用是理解此功能的一个很好的用例。

另外我必须提到,如果您要搜索其他语言的 Comet-chat 实现,您可能会注意到不使用 MQ 的简单实现。那么他们如何交换信息呢?问题是这样的解决方案通常被实现为独立的单线程异步服务器,因此它们可以将所有连接存储在线程本地数组(或类似的东西)中,在一个循环中处理多个连接,并在需要时选择一个并通知。这种异步服务器实现是一种非常适合 Comet-technique 的现代方法。但是,您很可能在 mod_php 或 FastCGI 之上实现 Comet,在这种情况下,这种简单的方法不适合您,您应该使用 MQ。

这对于理解如何实现一个独立的异步 Comet 服务器以在单个线程中处理多个连接仍然非常有用。最新版本的 PHP 支持 Libevent 和 Socket Streams,因此也可以在 PHP 中实现这种服务器。 PHP 文档中还有一个example。

如何在 Ajax 连接仍处于活动状态时检查结果?我知道 jQuery 对 ajax 调用的成功功能,但是如何在连接仍在进行时检查数据?

如果您使用普通的 Ajax 技术(例如普通 XHR、jQuery Ajax 等)进行长时间运行的轮询,则没有一种简单的方法可以在单个 Ajax 请求中传输多个响应。正如您所提到的,您只有“成功”处理程序来处理整个响应,而不是它的一部分。作为一种解决方法,人们每个请求只发送一个响应并在“成功”处理程序中处理它,之后他们只是打开一个新的长轮询请求。这就是 HTTP 协议的工作原理。

还应该提到的是,实际上有一些解决方法可以使用各种技术来实现类似流的功能,这些技术使用隐藏的IFRAME 中的无限长页面或使用多部分 HTTP 响应等技术。这两种方法都有一定的缺点(前一种被认为是不可靠的,有时可能会产生不必要的浏览器行为,例如无限加载指示器,而后一种会泄漏一致且直接的跨浏览器支持,但是已知某些应用程序仍然成功地依赖它当浏览器无法正确处理多部分响应时,机制会退回到长轮询)。

如果您想以可靠的方式处理每个请求/连接的多个响应,您应该考虑使用更先进的技术,例如 WebSocket,它受最新浏览器或任何支持原始套接字的平台(例如例如 Flash 或如果您为移动应用程序开发)。

您能否详细说明消息队列?

Message Queue 是一个描述Observer pattern(也称为“发布/订阅”或简称为 PubSub)的独立(或内置)实现的术语。如果您开发一个大型应用程序,拥有一个非常很有用 - 它允许您解耦系统的不同部分,实现事件驱动的异步设计并使您的生活更轻松,尤其是在异构系统中.它对现实世界的系统有很多应用,我只提几个:

任务队列。假设我们正在编写自己的 YouTube,并且需要在后台转换用户的视频文件。我们显然应该有一个带有 UI 的 web 应用程序来上传电影和一些固定数量的工作进程来转换视频文件(也许我们甚至需要一些专用服务器,我们的工作人员只会离开这些服务器)。此外,我们可能不得不用 C 编写我们的工人以确保更好的性能。我们所要做的只是设置一个消息队列服务器来收集来自 webapp 的视频转换任务并将其传递给我们的工作人员。当 worker 生成时,它连接到 MQ 并空闲等待新任务。当有人上传视频文件时,webapp 连接到 MQ 并发布一条带有新作业的消息。强大的 MQ,如RabbitMQ 可以在连接的工作人员之间平均分配任务,跟踪已完成的任务,确保不会丢失任何内容,并提供故障转移甚至管理 UI 来浏览当前待处理的任务和统计信息。 异步行为。我们的彗星聊天就是一个很好的例子。显然,我们不想一直定期轮询我们的数据库(那么 Comet 有什么用呢?——与定期 Ajax 请求没有太大区别)。我们宁愿需要有人在出现新的聊天消息时通知我们。消息队列就是那个人。假设我们正在使用Redis 键/值存储——这是一个非常棒的工具,它在其数据存储功能中提供了PubSub 实现。最简单的场景可能如下所示:
    有人进入聊天室后,将发出新的 Ajax 长轮询请求。 服务器端的请求处理程序向 Redis 发出命令以订阅“newmessage”频道。 一旦有人在他的聊天中输入了一条消息,服务器端处理程序就会将一条消息发布到 Redis 的“newmessage”主题中。 发布消息后,Redis 将立即通知所有之前订阅该频道的待处理处理程序。 在通知保持长轮询请求打开的 PHP 代码时,可以使用新的聊天消息返回请求,因此将通知所有用户。他们可以在那时从数据库中读取新消息,或者这些消息可以直接在消息负载中传输。

我希望我的插图易于理解,但是消息队列是一个非常广泛的主题,因此请参阅上面提到的资源以进一步阅读。

【讨论】:

+1 纯粹的努力 :) 您能否详细说明消息队列?这是我第一次听说。 np。 MQ 实际上有点不同(而且很长),尽管我添加了另一段对该主题进行了非常基本的介绍。 我认为这是一个新的伟大社区成员的开始。当我在电脑旁边时,我会检查一下,因为现在我在手机上。欢迎来到堆栈溢出,并保持出色的工作。 嗯,我目前正在学习 Java 以更好地理解这个概念,我会接受你的回答,非常感谢 :) @coffeesnake - 您是否有机会根据我在下面提供的信息更正您的答案?如果你这样做,我会删除我的答案。【参考方案2】:

如何在 Ajax 连接仍处于活动状态时检查结果?我知道 jQuery 对 ajax 调用的成功功能,但是如何在连接仍在进行时检查数据?

其实可以的。我已经为上述内容提供了修改后的答案,但我不知道它是否仍在等待处理或已被忽略。在此处提供更新,以便提供正确的信息。

如果您保持客户端和服务器之间的连接打开,则可以推送附加到响应的更新。随着每次更新的到来,XMLHttpRequest.onreadystatechange 事件被触发,XMLHttpRequest.readyState 的值将为 3。这意味着 XMLHttpRequest.responseText 继续增长。

你可以在这里看到一个例子: http://www.leggetter.co.uk/***/7213549/

要查看 JS 代码,只需查看源代码。 PHP代码是:

<?php
$updates = $_GET['updates'];
if(!$updates) 
  $updates = 100;


header('Content-type: text/plain');
echo str_pad('PADDING', 2048, '|PADDING'); // initial buffer required

$sleep_time = 1;
$count = 0;
$update_suffix = 'Just keep streaming, streaming, streaming. Just keep streaming.';
while($count < 100) 
  $message = $count . ' >> ' . $update_suffix;
  echo($message);
  flush();
  $count = $count + 1;
  sleep($sleep_time);

?>

在基于 Gecko 的浏览器(例如 Firefox)中,可以使用 multipart/x-mixed-replace 完全替换 responseText。我没有提供这方面的例子。

使用jQuery.ajax 似乎无法实现相同的功能。只要onreadystatechange 事件被触发,success 回调就不会触发。这是令人惊讶的,因为文档指出:

但是没有提供onreadystatechange机制,因为success、error、complete和statusCode涵盖了所有可能的需求。

所以除非我误解了文档,否则文档可能是错误的?

您可以在这里看到一个尝试使用 jQuery 的示例: http://www.leggetter.co.uk/***/7213549/jquery.html

如果您查看 Firebug 或 Chrome 开发人员工具中的网络选项卡,您会看到 stream.php 的文件大小在增长,但 success 回调仍未触发。

【讨论】:

嗯,这很奇怪,我不知道它会发生这种情况(我实际上不认为它会发生这种情况),你能准备一个例子吗? (它应该适用于最新的 Firefox 和 Chrome)。 好的,用示例链接更新了我的回复。看起来 jQuery.ajax 没有 onreadystatechange 事件。 正如我上面提到的,这种方式不可靠,我强烈建议使用为实现此类功能而设计的东西,例如 WebSockets。顺便说一句,您可以在打开请求时接收 XHR 对象并通过计时器检查其就绪状态以解决该 jQuery 限制。 您的答案已损坏,它仅在脚本末尾输出所有内容。你能修好吗? @leggetter 我的意思是第一个应该增长但没有增长的例子(leggetter.co.uk/***/7213549)

以上是关于长轮询/HTTP 流一般问题的主要内容,如果未能解决你的问题,请参考以下文章

Libevent + Comet(长轮询/Http-Stream)

Tornado-长轮询_webSocket

轮询与长轮询[关闭]

HTTP协议中的短轮询长轮询长连接和短连接

http 长连接,长轮询区别

长轮询 VS 短轮询