facebook,gmail如何发送实时通知?

Bil*_*lly 265 comet

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

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

如果没有客户端交互,GMail Chat如何能够发出AJAX请求?

我想知道是否有任何代码引用,我可以遵循写一个非常简单的例子.许多帖子或网站只是谈论这项技术.很难找到完整的示例代码.此外,似乎可以使用许多方法来实现彗星,例如Hidden IFrame,XMLHttpRequest.在我看来,使用XMLHttpRequest是一个更好的选择.您如何看待不同方法的优缺点?Gmail使用哪一个?

我知道它需要在服务器端和客户端都这样做.有没有PHP和Javascript示例代码?

Kaz*_*zar 424

Facebook这样做的方式非常有趣.

执行此类通知的常用方法是在给定时间间隔(可能每隔几秒)轮询服务器上的脚本(使用AJAX),以检查是否发生了某些事情.但是,这可能是网络密集型的,并且您经常提出无意义的请求,因为什么也没发生.

Facebook所采用的方式是使用彗星方法,而不是轮询间隔,一旦一个民意调查完成,它就会发出另一个民意调查.但是,对服务器上的脚本的每个请求都具有极长的超时,并且服务器仅在发生某些事件时才响应该请求.如果你在Facebook上调出Firebug的控制台选项卡,你可以看到这种情况发生,对脚本的请求可能需要几分钟.它真的非常巧妙,因为这种方法会立即减少请求数量,以及发送它们的频率.您现在有效地拥有一个允许服务器"触发"事件的事件框架.

在此背后,就这些民意调查返回的实际内容而言,它是一个JSON响应,其中包含的事件列表和相关信息.它虽然缩小了,所以有点难以阅读.

就实际技术而言,AJAX是走到这里的方式,因为您可以控制请求超时等许多其他事情.我建议使用jQuery来执行AJAX(这里的堆栈溢出陈词滥调),它会带来很多交叉兼容性问题.就PHP而言,您可以简单地轮询PHP脚本中的事件日志数据库表,并仅在发生某些事情时返回到客户端?我希望有很多方法可以实现这一点.

实现:

服务器端:

在PHP中似乎有一些彗星库的实现,但说实话,它真的很简单,可能像下面的伪代码:

while(!has_event_happened()) {
   sleep(5);
}

echo json_encode(get_events());
Run Code Online (Sandbox Code Playgroud)
  • 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
});
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为将PHP标记为不能很好地扩展的语言/平台并不一定如此.它可用于开发极大规模的系统.看看facebook.如果开发人员做得对,那么它会扩展,如果没有,那么它就不会扩展.使用特定的Web平台并不能保证可伸缩性.哦,而且,问题确实要求PHP. (44认同)
  • @cHao:这是一个公平的观点,但是这个答案是在2009年开始使用hiphop之前写的.当时facebook仍然是一个非常大规模的系统,它使用自己的PHP. (14认同)
  • 因此,该技术是保持连接不断打开,这将使服务器保持恒定的压力.普通Web服务器的典型并发连接数约为200,但同时在线的Facebook用户数量则更大.他们是怎么做到的? (6认同)
  • @Kazar:"Facebook使用PHP"有点误导 - 最后我听说,他们开发HipHop是出于将PHP转换为C++的明确目的,因为PHP表现不佳. (4认同)
  • 这是一个非常好的和详细的解释.谢谢.您是否有许多实现代码的示例代码? (2认同)
  • @Paul-首先要考虑的是,实际上保持连接打开并不一定会使服务器承受持续的压力。我想象Facebook服务器将来自客户端的未决请求停放到某个地方,然后忘记了,直到线程唤醒,抓住未决请求并完成它。因此,高内存消耗,但不一定是高CPU。唯一真正的处理开销是在这种连接所需的任何TCP保持活动中,这将在网络级别进行处理。 (2认同)
  • @alexw-不会出现异常的原因是它实际上并不是递归(按正常意义)-随后对doPoll的调用位于回调函数中,该回调函数将在服务器回复后再运行响应(假设ajax设置未设置为“同步”,就永远不会设置为“同步”)。描述它的另一种方式是,如果在回调的doPoll函数上放置一个断点,则永远不会在其上方看到另一个doPoll条目。 (2认同)

Wal*_*ira 40

更新

随着我继续接受这方面的评价,我认为有理由记住这个答案是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);

}
Run Code Online (Sandbox Code Playgroud)

重要的是要记住(来自jQuery docs):

在jQuery 1.4.x及更低版本中,如果请求超时,XMLHttpRequest对象将处于无效状态; 访问任何对象成员可能会抛出异常.仅在Firefox 3.0+中,超时无法取消脚本和JSONP请求; 即使脚本在超时期限后到达,脚本也会运行.

长轮询 - 服务器

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

function handleRequest () {  

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

     return events();

} 
Run Code Online (Sandbox Code Playgroud)

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

它确实解决了这个问题,但是,如果你像我在研究时一样关注可扩展性和性能,你可能会考虑我发现的另一个解决方案.

使用套接字!

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

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

如果您不喜欢这个解决方案,请尝试APE(Ajax Push Engine).

希望我帮忙.


ide*_*awu 18

根据Facebook的消息系统幻灯片,Facebook使用彗星技术将信息"推送"到网络浏览器.Facebook的彗星服务器建立在开源的Erlang网络服务器mochiweb上.

在下图中,短语"频道群集"表示"彗星服务器".

系统总览

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

您可以尝试icomet,一个C1000K C++与libevent的内置彗星服务器.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);
    }
});
Run Code Online (Sandbox Code Playgroud)

icomet支持各种浏览器和操作系统,包括Safari(iOS,Mac),IE(Windows),Firefox,Chrome等.


abh*_*nav 5

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

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

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

  • 此外,谷歌使用GCM服务的Android,开发人员可以使用它来实现推送消息服务.http://developer.android.com/google/gcm/index.html如果您觉得答案有用,请接受. (3认同)

Ron*_*enz 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
Run Code Online (Sandbox Code Playgroud)