19

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

长轮询

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

使用 HTTP 流

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

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

4

2 回答 2

25

是的,类似彗星的技术通常在一开始就炸毁大脑——只是让你以不同的方式思考。另一个问题是 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 文档中还提供了一个示例。

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

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

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

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

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

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

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

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

于 2011-08-27T12:17:08.043 回答
5

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

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

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

你可以在这里看到一个例子: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)中,可以responseText使用multipart/x-mixed-replace. 我没有提供这方面的例子。

看起来不可能使用jQuery.ajax. 每当触发事件时,success回调都不会onreadystatechange触发。这是令人惊讶的,因为文档指出:

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

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

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

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

于 2011-09-08T12:16:24.703 回答