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

Posted

技术标签:

【中文标题】facebook、gmail如何发送实时通知?【英文标题】:How does facebook, gmail send the real time notification? 【发布时间】:2010-11-08 08:29:06 【问题描述】:

我已经阅读了一些关于这个主题的帖子,答案是彗星、反向 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 使用哪一种?

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

【问题讨论】:

【参考方案1】:

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

执行此类通知的常用方法是在给定的时间间隔(可能每隔几秒)轮询服务器上的脚本(使用 AJAX),以检查是否发生了某些事情。但是,这可能会占用大量网络资源,而且您经常会发出毫无意义的请求,因为什么都没发生。

Facebook 这样做的方式是使用彗星方法,而不是按时间间隔进行轮询,一旦一个轮询完成,它就会发出另一个轮询。但是,对服务器上脚本的每个请求都有一个非常长的超时时间,服务器只有在发生某些事情时才会响应请求。如果您在 Facebook 上打开 Firebug 的“控制台”选项卡,您可以看到这种情况,对脚本的请求可能需要几分钟。这真的很巧妙,因为这种方法会立即减少请求的数量以及发送请求的频率。您现在有效地拥有一个允许服务器“触发”事件的事件框架。

在这之后,就从这些民意调查返回的实际内容而言,它是一个 JSON 响应,其中包含一个事件列表以及有关它们的信息。不过它被缩小了,所以有点难以阅读。

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

实施:

服务器端:

comet 库在 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
);

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

【讨论】:

这是一个非常好的和详细的解释。谢谢你。您是否有用于实现它的多种方法之一的示例代码? 我认为将 PHP 标记为不能很好扩展的语言/平台不一定是正确的。它可用于开发超大规模系统。看脸书。如果开发人员做得对,那么它会扩展,如果不是,那么它就不会。使用特定的 Web 平台并不能保证可扩展性。哦,而且,这个问题确实要求 PHP。 @Kazar:“Facebook 使用 PHP”有点误导——上次我听说,他们开发 HipHop 是为了将 PHP 转换为 C++,因为 PHP 的性能不够好。跨度> @cHao:这是一个公平的观点,但是这个答案是在 2009 年写的,在 facebook 开始使用 hiphop 之前。当时 facebook 仍然是一个非常庞大的系统,它自己使用 php。 所以技术是保持连接不断打开,这将使服务器保持恒定的压力。一个普通网络服务器的典型并发连接数约为 200,但同时在线的 Facebook 用户数量要大得多。他们是怎么做到的?【参考方案2】:

更新

随着我继续收到对此的支持,我认为记住这个答案已有 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)。

希望我能帮上忙。

【讨论】:

您认为 1 可以替代另一个,还是在同一个项目中需要两种技术? 如果你的意思是APE和NodeJS,你可以选择其中之一。如果您的意思是定期 AJAX 请求和我建议的请求,我的解决方案可能会在缺少套接字支持时回退到 ajax 请求(请参阅 socket.io 文档)。在这两种情况下,您只需要一种解决方案。 嘿沃尔特,我想在我的一个网站上使用您的建议。你知道我在哪里可以获得 Sockets 服务器吗?谢谢! 你可以实现它。 Node 让它变得非常简单。 如何检测hasTimedOut()【参考方案3】:

根据slideshow about Facebook's Messaging system,Facebook 使用彗星技术将消息“推送”到网络浏览器。 Facebook 的彗星服务器建立在开源的 Erlang 网络服务器 mochiweb 之上。

在下图中,“通道集群”表示“彗星服务器”。

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

你可以试试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 如何调整到这个特定的对话并将消息推送到两端? (只是一个猜测:我只能想象应用程序打开一个套接字并绑定两个客户端地址,然后只要在框中写入消息就继续监听和写入)【参考方案4】:

Facebook 使用 MQTT 而不是 HTTP。推送优于轮询。 通过 HTTP,我们需要不断地轮询服务器,但通过 MQTT 服务器将消息推送到客户端。

MQTT与HTTP对比:http://www.youtube.com/watch?v=-KNPXPmx88E

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

【讨论】:

另外,google使用了android的GCM服务,开发者可以使用它来实现推送消息服务。 developer.android.com/google/gcm/index.html如果您觉得答案有用,请采纳。【参考方案5】:

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

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

    网络错误或执行错误。这是客户端应该优雅地接受并等待服务器恢复联机的实际错误。

主要问题是,如果您的错误处理程序立即重新建立连接,也为类型 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

【讨论】:

以上是关于facebook、gmail如何发送实时通知?的主要内容,如果未能解决你的问题,请参考以下文章

Android : 如何使用我的应用程序中的 whatsapp、facebook、gmail 等发送应用程序邀请消息

GMail 停止发送推送通知

配置 Gmail 帐户以向主题发送邮箱更新通知

如何实现类似facebook的通知?

如何为使用 Facebook 登录的用户发送游戏请求推送通知?

如何在 iPhone 应用中实现 Facebook 和 Twitter 通知表