我已经阅读了一些关于这个主题的帖子,答案是彗星、反向 ajax、http 流、服务器推送等。
How does incoming mail notification on Gmail works?
How is GMail Chat able to make AJAX requests without client interaction?
我想知道是否有任何代码参考可以用来编写一个非常简单的示例。许多帖子或网站只是谈论这项技术。很难找到完整的示例代码。此外,似乎可以使用许多方法来实现彗星,例如 Hidden IFrame、XMLHttpRequest。在我看来,使用 XMLHttpRequest 是一个更好的选择。您如何看待不同方法的优缺点? Gmail 使用哪一种?
我知道它需要在服务器端和客户端都这样做。是否有任何 PHP 和 Javascript 示例代码?
Facebook 这样做的方式非常有趣。
执行此类通知的常用方法是在给定的时间间隔(可能每隔几秒)轮询服务器上的脚本(使用 AJAX),以检查是否发生了某些事情。但是,这可能会占用大量网络资源,并且您经常会发出毫无意义的请求,因为什么都没有发生。
Facebook 这样做的方式是使用彗星方法,而不是间隔轮询,一旦一个轮询完成,它就会发出另一个轮询。但是,对服务器上脚本的每个请求都有一个非常长的超时时间,服务器只有在发生某些事情时才会响应请求。如果您在 Facebook 上打开 Firebug 的“控制台”选项卡,您可以看到这种情况,对脚本的请求可能需要几分钟。这真的很巧妙,因为这种方法会立即减少请求的数量以及发送请求的频率。您现在有效地拥有一个允许服务器“触发”事件的事件框架。
在这背后,就从这些民意调查返回的实际内容而言,它是一个 JSON 响应,其中包含事件列表以及有关它们的信息。虽然它被缩小了,所以有点难以阅读。
就实际技术而言,AJAX 是要走的路,因为您可以控制请求超时以及许多其他事情。我建议(这里的堆栈溢出陈词滥调)使用 jQuery 来执行 AJAX,它会消除很多交叉兼容性问题。就 PHP 而言,您可以简单地在 PHP 脚本中轮询事件日志数据库表,并且仅在发生某些事情时才返回客户端?我希望有很多方法可以实现这一点。
实施:
服务器端:
在 PHP 中似乎有一些彗星库的实现,但老实说,它确实非常简单,可能类似于以下伪代码:
while(!has_event_happened()) {
sleep(5);
}
echo json_encode(get_events());
has_event_happened 函数只会检查事件表中是否发生了任何事情,然后 get_events 函数会返回表中新行的列表?真的取决于问题的上下文。
不要忘记更改您的 PHP 最大执行时间,否则它会提前超时!
客户端:
看一下用于 Comet 交互的 jQuery 插件:
项目主页:http://plugins.jquery.com/project/Comet
Google 代码:https://code.google.com/archive/p/jquerycomet/ - 似乎在 subversion 存储库中有某种示例用法。
也就是说,该插件似乎增加了相当多的复杂性,它在客户端上确实非常简单,也许(使用 jQuery)类似于:
function doPoll() {
$.get("events.php", {}, function(result) {
$.each(result.events, function(event) { //iterate over the events
//do something with your event
});
doPoll();
//this effectively causes the poll to run again as
//soon as the response comes back
}, 'json');
}
$(document).ready(function() {
$.ajaxSetup({
timeout: 1000*60//set a global AJAX timeout of a minute
});
doPoll(); // do the first poll
});
整个事情很大程度上取决于您现有架构的组合方式。
更新
随着我继续收到对此的支持,我认为记住这个答案已有 4 年历史是合理的。网络的发展速度非常快,所以请注意这个答案。
我最近遇到了同样的问题并研究了这个主题。
给出的解决方案称为长轮询,要正确使用它,您必须确保您的 AJAX 请求具有“大”超时,并且始终在当前结束(超时、错误或成功)之后发出此请求。
长轮询 - 客户端
在这里,为了保持代码简短,我将使用 jQuery:
function pollTask() {
$.ajax({
url: '/api/Polling',
async: true, // by default, it's async, but...
dataType: 'json', // or the dataType you are working with
timeout: 10000, // IMPORTANT! this is a 10 seconds timeout
cache: false
}).done(function (eventList) {
// Handle your data here
var data;
for (var eventName in eventList) {
data = eventList[eventName];
dispatcher.handle(eventName, data); // handle the `eventName` with `data`
}
}).always(pollTask);
}
重要的是要记住(来自 jQuery docs):
在 jQuery 1.4.x 及以下版本中,如果请求超时,XMLHttpRequest 对象将处于无效状态;访问任何对象成员都可能引发异常。仅在 Firefox 3.0+ 中,脚本和 JSONP 请求不能被超时取消;即使脚本在超时期限之后到达,它也会运行。
长轮询 - 服务器
它没有任何特定的语言,但它会是这样的:
function handleRequest () {
while (!anythingHappened() || hasTimedOut()) { sleep(2); }
return events();
}
在这里,hasTimedOut
将确保您的代码不会永远等待,而 anythingHappened
将检查是否发生了任何事件。 sleep
用于在没有任何反应的情况下释放您的线程以执行其他操作。 events
将以 JSON 格式(或您喜欢的任何其他格式)返回事件字典(或您可能喜欢的任何其他数据结构)。
它确实可以解决问题,但是,如果您像我在研究时一样担心可扩展性和性能,您可能会考虑我找到的另一种解决方案。
解决方案
使用插座!
在客户端,为避免任何兼容性问题,请使用 socket.io。它尝试直接使用套接字,并在套接字不可用时回退到其他解决方案。
在服务器端,使用 NodeJS 创建一个服务器(示例 here)。客户端将订阅与服务器一起创建的这个频道(观察者)。每当必须发送通知时,它都会在此频道中发布,并且订阅者(客户端)会收到通知。
如果您不喜欢此解决方案,请尝试 APE (Ajax Push Engine)。
希望我有所帮助。
hasTimedOut()
?
根据 slideshow about Facebook's Messaging system,Facebook 使用彗星技术将消息“推送”到网络浏览器。 Facebook 的彗星服务器建立在开源的 Erlang 网络服务器 mochiweb 之上。
在下图中,“通道集群”一词的意思是“彗星服务器”。
https://i.stack.imgur.com/9Craq.jpg
许多其他大型网站都建立了自己的彗星服务器,因为每个公司的需求都存在差异。但是在开源彗星服务器上构建自己的彗星服务器是一个好方法。
您可以尝试 icomet,这是一个使用 libevent 构建的 C1000K C++ comet 服务器。 icomet 还提供了一个 JavaScript 库,使用起来非常简单:
var comet = new iComet({
sign_url: 'http://' + app_host + '/sign?obj=' + obj,
sub_url: 'http://' + icomet_host + '/sub',
callback: function(msg){
// on server push
alert(msg.content);
}
});
icomet 支持广泛的浏览器和操作系统,包括 Safari(iOS、Mac)、IE(Windows)、Firefox、Chrome 等。
Facebook 使用 MQTT 而不是 HTTP。推送优于轮询。通过 HTTP,我们需要不断地轮询服务器,但通过 MQTT 服务器将消息推送到客户端。
MQTT 与 HTTP 的比较:http://www.youtube.com/watch?v=-KNPXPmx88E
注意:我的答案最适合移动设备。
长轮询的一个重要问题是错误处理。有两种类型的错误:
请求可能会超时,在这种情况下客户端应立即重新建立连接。当没有消息到达时,这是长轮询中的正常事件。网络错误或执行错误。这是一个实际的错误,客户端应该优雅地接受并等待服务器重新上线。
主要问题是,如果您的错误处理程序也立即为类型 2 错误重新建立连接,则客户端将 DOS 服务器。
代码示例的两个答案都错过了这一点。
function longPoll() {
var shouldDelay = false;
$.ajax({
url: 'poll.php',
async: true, // by default, it's async, but...
dataType: 'json', // or the dataType you are working with
timeout: 10000, // IMPORTANT! this is a 10 seconds timeout
cache: false
}).done(function (data, textStatus, jqXHR) {
// do something with data...
}).fail(function (jqXHR, textStatus, errorThrown ) {
shouldDelay = textStatus !== "timeout";
}).always(function() {
// in case of network error. throttle otherwise we DOS ourselves. If it was a timeout, its normal operation. go again.
var delay = shouldDelay ? 10000: 0;
window.setTimeout(longPoll, delay);
});
}
longPoll(); //fire first handler
不定期副业成功案例分享