长轮询/HTTP 流式处理一般问题

Long Polling/HTTP Streaming General Questions

提问人:Madara's Ghost 提问时间:8/27/2011 最后编辑:CommunityMadara's Ghost 更新时间:9/12/2011 访问量:7309

问:

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

使用长轮询

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

使用 HTTP 流式处理

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

我将不胜感激任何和所有答案,提前致谢。

php jquery 彗星 长轮询 http-streaming

评论

0赞 Jacco 8/27/2011
我的学院从事一个使用长轮询请求(几个小时)的项目。在尝试了几种技术后,他们最终使用了轨道。我不知道细节,只知道应用程序现在非常稳定,他们对此感到满意。
0赞 Madara's Ghost 8/27/2011
除了我使用的是 PHP,尽管我也想了解它。
0赞 Jacco 8/28/2011
他们决定使用 Orbited 作为中间服务。该应用程序的主要部分是 PHP,带有 javascript(html等)前端。

答:

25赞 coffeesnake 8/27/2011 #1

是的,类似彗星的技术通常会在一开始就炸毁大脑——只是让你以不同的方式思考。另一个问题是没有那么多资源可用于PHP,因为每个人都在node.js,Python,Java等中做他们的Comet。

我会尝试回答你的问题,希望它能为人们提供一些关于这个话题的启示。

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

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

因此,您无需连续查询数据库,只需订阅 MQ 事件并挂起,直到其他人发布您订阅的消息,MQ 将唤醒您并发送消息。聊天应用程序是理解此功能的一个非常好的用例。

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

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

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

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

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

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

您能详细说明一下消息队列吗?

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

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

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

评论

0赞 Madara's Ghost 8/27/2011
+1 纯粹努力 :)您能详细说明一下消息队列吗?这是我第一次听说它。
0赞 coffeesnake 8/27/2011
NP的。MQ 实际上有点不同(而且很长),尽管我添加了另一段,其中包含对该主题的非常基本的介绍。
0赞 Madara's Ghost 8/28/2011
我认为这是一个新的伟大社区成员的开始。当我在电脑旁边时,我会检查一下,因为现在我在手机上。欢迎来到 Stack Overflow,并继续努力。
0赞 Madara's Ghost 8/29/2011
好吧,我目前正在学习 Java 以更好地理解这个概念,我会接受你的回答,非常感谢:)
0赞 leggetter 9/8/2011
@coffeesnake - 您有机会根据我在下面提供的信息更正您的答案吗?如果你这样做,我会删除我的答案。
5赞 leggetter 9/8/2011 #2

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

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

如果保持客户端和服务器之间的连接处于打开状态,则可以推送更新,通过这些更新将附加到响应中。当每次更新时,都会触发事件,并且 的值将为 3。这意味着 XMLHttpRequest.responseText 会继续增长。XMLHttpRequest.onreadystatechangeXMLHttpRequest.readyState

您可以在此处查看示例:http://www.leggetter.co.uk/stackoverflow/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)中,可以使用 .我没有提供这方面的例子。responseTextmultipart/x-mixed-replace

看起来不可能使用 实现相同类型的功能。每当触发事件时,回调都不会触发。这是令人惊讶的,因为文档指出:jQuery.ajaxsuccessonreadystatechange

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

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

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

如果您查看 Firebug 或 Chrome 开发者工具中的网络选项卡,您会看到文件大小在增长,但回调仍然没有触发。stream.phpsuccess

评论

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