Facebook,Gmail如何发送实时通知?


269

我已经阅读了有关此主题的一些帖子,答案是彗星,反向ajax,http流,服务器推送等。

Gmail上的传入邮件通知如何工作?

在没有客户端交互的情况下,GMail Chat如何能够发出AJAX请求?

我想知道是否有任何代码参考可以用来编写一个非常简单的示例。许多帖子或网站只是在谈论这项技术。很难找到完整的示例代码。同样,似乎可以使用多种方法来实现彗星,例如Hidden IFrame,XMLHttpRequest。我认为,使用XMLHttpRequest是更好的选择。您如何看待不同方法的利弊?Gmail使用哪一个?

我知道它需要同时在服务器端和客户端执行此操作。是否有PHP和Javascript示例代码?

Answers:


428

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
});

整个过程很大程度上取决于现有架构的组合方式。


2
这是一个非常不错的详细说明。谢谢。您是否有示例代码可以实现多种方法之一?
比利(Billy)

45
我认为将PHP标记为无法很好扩展的语言/平台不一定是正确的。它可以用于开发超大型系统。看facebook。如果开发人员做对了,那么它将扩展,如果不能,那么就不会。使用特定的Web平台并不能保证可伸缩性。哦,而且,这个问题确实要求使用PHP。
Alistair Evans 2010年

5
@Kazar:“ Facebook使用PHP”有点误导-上一次我听说,他们开发了HipHop是为了将PHP转换为C ++的明确目的,因为PHP的性能还不够好。
cHao 2011年

14
@cHao:这是一个公平的观点,但是这个答案写于2009年,当时Facebook开始使用嘻哈音乐。当时,facebook仍然是使用php的非常大规模的系统。
阿利斯泰尔·埃文斯

6
因此,该技术是保持连接一直保持打开状态,这将使服务器保持恒定的压力。一个普通的Web服务器的典型并发连接数量约为200,但是同时在线的Facebook用户数量要大得多。他们是如何做到的?
保罗

43

更新资料

当我继续对此表示赞同时,我认为有理由记住这个答案已有4年历史了。Web的增长速度非常快,因此请注意这个答案。


我最近也遇到过同样的问题,并对这个问题进行了研究。

给定的解决方案称为长轮询,要正确使用该解决方案,必须确保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对象将处于无效状态;否则,XMLHttpRequest对象将处于无效状态。访问任何对象成员都可能引发异常。仅在Firefox 3.0+中,无法通过超时取消脚本和JSONP请求;即使脚本在超时期限后到达,脚本也将运行。

长轮询-服务器

它没有任何特定的语言,但是会是这样的:

function handleRequest () {  

     while (!anythingHappened() || hasTimedOut()) { sleep(2); }

     return events();

} 

在这里,hasTimedOut将确保您的代码不会永远等待,并且anythingHappened将检查是否发生了任何事件。该sleep是你释放线程做其他的东西,而没有任何反应。的events会返回事件的JSON格式的词典(或你可能更喜欢任何其他数据结构)(或任何其他你喜欢)。

它确实可以解决问题,但是,如果像我在研究时一样担心可伸缩性和性能,则可以考虑使用我发现的另一种解决方案。

使用插座!

在客户端,为了避免任何兼容性问题,请使用socket.io。它尝试直接使用套接字,并且在套接字不可用时回退到其他解决方案。

在服务器端,使用NodeJS创建服务器(此处为示例)。客户端将订阅服务器创建的该频道(观察者)。每当必须发送通知时,它就会在此通道中发布,并通知下标(客户端)。

如果您不喜欢这种解决方案,请尝试使用APE(Ajax Push Engine)。

希望我能帮上忙。


您是否认为1是另一种的替代品,还是在同一项目中同时需要两种技术?
TQ

如果您是指APE和NodeJS,则可以选择其中之一。如果您是指定期的AJAX请求和我建议的请求,那么当缺乏套接字支持时,我的解决方案可能会退回到ajax的请求(请参阅socket.io文档)。在这两种情况下,您只需要一种解决方案。
Walter Macambira

嘿沃尔特,我想在我的一个网站上使用您的建议。您知道我可以在哪里获得Sockets服务器吗?谢谢!
Progo 2014年

1
您可以实现它。Node使其非常简单。
Walter Macambira 2014年

如何检测hasTimedOut()
Mobasher Fasihy

18

根据有关Facebook消息系统幻灯片显示,Facebook使用彗星技术将消息“推送”到Web浏览器。Facebook的彗星服务器基于开源的Erlang Web服务器mochiweb构建。

在下面的图片中,短语“通道群集”表示“竞争服务器”。

系统总览

许多其他大型网站都构建自己的彗星服务器,因为每个公司的需求之间存在差异。但是,在开源彗星服务器上构建自己的彗星服务器是一种不错的方法。

您可以尝试icomet,它是使用libevent构建的C1000K C ++彗星服务器。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如何调入这个特定的对话并将消息推送到两端?(只是一个猜测:我只能想象应用程序打开一个套接字并绑定两个客户端地址,然后只要在框中写消息就保持侦听和写)
edam

5

Facebook使用MQTT而不是HTTP。推送比轮询更好。通过HTTP,我们需要连续轮询服务器,但是通过MQTT服务器将消息推送到客户端。

MQTT和HTTP之间的比较:http : //www.youtube.com/watch?v= -KNPXPmx88E

注意:我的答案最适合移动设备。


3
此外,谷歌为Android使用GCM服务,开发人员可以将其用于实现推送消息服务。developer.android.com/google/gcm/index.html 如果发现有用的答案,请接受。
2013年

5

长轮询的一个重要问题是错误处理。错误有两种:

  1. 该请求可能会超时,在这种情况下,客户端应立即重新建立连接。当没有消息到达时,这是长轮询中的正常事件。

  2. 网络错误或执行错误。这是一个实际错误,客户端应正常接受并等待服务器重新联机。

主要问题是,如果您的错误处理程序也立即针对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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.