我已经阅读了一些关于这个主题的帖子,答案是彗星,反向ajax,http流,服务器推送等。
How does incoming mail notification on Gmail works?
How is GMail Chat able to make AJAX requests without client interaction?
我想知道是否有任何代码引用可以用来编写一个非常简单的示例。许多帖子或网站只是谈论这项技术。很难找到完整的示例代码。此外,似乎可以使用许多方法来实现彗星,例如,隐藏的IFrame,XMLHttpRequest。在我看来,使用XMLHttpRequest是一个更好的选择。您如何看待不同方法的优缺点? Gmail使用哪一个?
我知道它需要在服务器端和客户端都这样做。 有没有PHP和Javascript示例代码?
答案 0 :(得分:418)
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插件:
也就是说,插件似乎增加了一些复杂性,它在客户端上非常简单,也许(使用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
});
整个过程很大程度上取决于您现有架构的组合方式。
答案 1 :(得分:39)
当我继续接受对此的赞成时,我认为有理由记住这个答案是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)。
希望我帮助过。
答案 2 :(得分:18)
根据slideshow about Facebook's Messaging system,Facebook使用彗星技术将信息“推送”到网络浏览器。 Facebook的彗星服务器建立在开源的Erlang网络服务器mochiweb上。
在下图中,短语“channel clusters”表示“彗星服务器”。
许多其他大型网站都建立了自己的彗星服务器,因为每个公司的需求都存在差异。但是在开源彗星服务器上构建自己的彗星服务器是一种很好的方法。
您可以尝试使用libevent构建的C1000K C ++彗星服务器icomet。 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等。
答案 3 :(得分:5)
Facebook使用MQTT而不是HTTP。推送比轮询更好。 通过HTTP,我们需要连续轮询服务器,但是通过MQTT服务器将消息推送到客户端。
MQTT和HTTP之间的比较:http://www.youtube.com/watch?v=-KNPXPmx88E
注意:我的答案最适合移动设备。
答案 4 :(得分:5)
长轮询的一个重要问题是错误处理。 有两种类型的错误:
请求可能会超时,在这种情况下,客户端应立即重新建立连接。当没有消息到达时,这是长轮询中的正常事件。
网络错误或执行错误。这是客户端应该优雅接受的实际错误,并等待服务器重新联机。
主要问题是,如果您的错误处理程序也会立即为类型2错误重新建立连接,则客户端将DOS服务器。
代码示例的两个答案都错过了这个。
$_SESSION['myusername']