Facebook, Gmail gerçek zamanlı bildirimi nasıl gönderir?


269

Bu konu hakkında bazı yazılar okudum ve cevaplar kuyruklu yıldız, ters ajax, http streaming, sunucu push, vb.

Gmail'de gelen posta bildirimi nasıl çalışır?

GMail Chat, istemci etkileşimi olmadan AJAX isteklerini nasıl yapabilir?

Çok basit bir örnek yazmak için takip edebileceğiniz herhangi bir kod referansları olup olmadığını bilmek istiyorum. Birçok yayın veya web sitesi teknoloji hakkında konuşur. Tam bir örnek kod bulmak zordur. Ayrıca, kuyruklu yıldızı uygulamak için birçok yöntem kullanılabileceği görülmektedir, örn. Hidden IFrame, XMLHttpRequest. Bence, XMLHttpRequest kullanmak daha iyi bir seçimdir. Farklı yöntemlerin artıları ve eksileri hakkında ne düşünüyorsunuz? Gmail hangisini kullanıyor?

Hem sunucu tarafında hem de istemci tarafında yapması gerektiğini biliyorum. Herhangi bir PHP ve Javascript örnek kodu var mı?

Yanıtlar:


428

Facebook'un bunu yapması oldukça ilginç.

Bu tür bildirimleri yapmanın yaygın bir yöntemi, sunucudaki bir komut dosyasını (AJAX kullanarak) belirli bir aralıkta (belki birkaç saniyede bir) yoklamak, bir şey olup olmadığını kontrol etmektir. Ancak, bu oldukça ağ yoğun olabilir ve çoğu zaman anlamsız istekler yaparsınız, çünkü hiçbir şey olmamıştır.

Facebook'un yaptığı gibi, bir ankette yoklamak yerine kuyruklu yıldız yaklaşımını kullanmaktır, bir anket tamamlanır tamamlanmaz başka bir tane yayınlar. Ancak, sunucudaki komut dosyasına yapılan her istek çok uzun bir zaman aşımına sahiptir ve sunucu isteğe yalnızca bir şey olduğunda yanıt verir. Facebook'ta Firebug'un Konsol sekmesini getirirseniz, bir komut dosyasının istekleri birkaç dakika sürebilirse bunun olduğunu görebilirsiniz. Gerçekten oldukça dahice, çünkü bu yöntem hem istek sayısını hem de ne sıklıkta göndermek zorunda olduğunuzu hemen kesiyor. Artık sunucunun olayları 'tetiklemesine' izin veren bir olay çerçeveniz var.

Bunun arkasında, bu anketlerden döndürülen gerçek içerik açısından, bir olay listesi ve bunlar hakkında bilgi gibi görünen bir JSON yanıtı var. Gerçi küçültülmüş, bu yüzden okunması biraz zor.

Gerçek teknoloji açısından, AJAX buraya gitmenin yoludur, çünkü istek zaman aşımlarını ve diğer birçok şeyi kontrol edebilirsiniz. AJAX yapmak için jQuery kullanarak (Burada yığın taşma klişe) tavsiye ederim, çapraz uyumluluk sorunları bir sürü alacak. PHP açısından, PHP betiğinizdeki bir olay günlüğü veritabanı tablosunu yoklayabilir ve yalnızca bir şey olduğunda istemciye dönebilirsiniz? Sanırım bunu uygulamanın birçok yolu var.

uygulanması:

Sunucu Tarafı:

PHP'de kuyruklu yıldız kitaplıklarının birkaç uygulaması var gibi görünüyor, ancak dürüst olmak gerekirse, gerçekten çok basit, belki de aşağıdaki sahte kod gibi bir şey:

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

echo json_encode(get_events());
  • Has_event_happened işlevi yalnızca olaylar tablosunda bir şey olup olmadığını kontrol eder ve sonra get_events işlevi tablodaki yeni satırların bir listesini döndürür mü? Sorunun bağlamına gerçekten bağlı.

  • PHP maksimum yürütme sürenizi değiştirmeyi unutmayın, aksi takdirde erken zaman aşımına uğrar!

Müşteri Tarafı:

Comet etkileşimi yapmak için jQuery eklentisine bir göz atın:

Bununla birlikte, eklenti adil bir karmaşıklık ekliyor gibi görünüyor, istemci üzerinde gerçekten çok basit, belki (jQuery ile) gibi bir şey:

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

Her şey mevcut mimarinizin nasıl bir araya getirildiğine bağlıdır.


2
Çok güzel ve ayrıntılı bir açıklama. Teşekkür ederim. Bunu uygulamanın birçok yolundan biri için örnek kodunuz var mı?
Billy

45
PHP'nin iyi ölçeklenmeyen bir dil / platform olarak etiketlenmesinin mutlaka doğru olmadığını düşünüyorum. Son derece büyük ölçekli sistemler geliştirmek için kullanılabilir. Facebook'a bak. Geliştirici doğru yaparsa, ölçeklenir, eğer değilse, o zaman olmaz. Belirli bir web platformunun kullanılması ölçeklenebilirlik garantisi değildir. Oh, ve ayrıca, soru PHP istedi.
Alistair Evans

5
@Kazar: "Facebook PHP kullanıyor" biraz yanıltıcı - son duydum, PHP yeterince iyi performans göstermediği için PHP'yi C ++ 'a dönüştürmek için HipHop'u geliştirdiler.
cHao

14
@cHao: Bu adil bir nokta, ancak bu cevap 2009 yılında, facebook hiphop kullanmaya başlamadan önce yazılmıştır. O zamanlar facebook hala kendi başına php kullanarak çok büyük ölçekli bir sistemdi.
Alistair Evans

6
Bu nedenle teknik, sunucuyu sürekli stres altında tutacak bir bağlantıyı sürekli açık tutmaktır. Ortalama bir web sunucusu için tipik bir eşzamanlı bağlantı miktarı yaklaşık 200'dür, ancak aynı anda çevrimiçi olan Facebook kullanıcılarının sayısı çok daha fazladır. Bunu nasıl yapıyorlar?
Paul

43

Güncelleme

Bu konuda oy almaya devam ederken, bu cevabın 4 yaşında olduğunu hatırlamanın makul olduğunu düşünüyorum. Web gerçekten hızlı bir şekilde büyüdü, bu yüzden lütfen bu cevaba dikkat edin.


Son zamanlarda aynı sorunu yaşadım ve konuyu araştırdım.

Verilen çözüm uzun yoklama olarak adlandırılır ve doğru bir şekilde kullanmak için AJAX isteğinizin "büyük" bir zaman aşımına sahip olduğundan emin olmanız ve bu isteği her zaman geçerli sona erdikten sonra (zaman aşımı, hata veya başarı) yapmanız gerekir.

Uzun Oylama - Müşteri

Burada, kodu kısa tutmak için jQuery kullanacağım:

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

}

Bunu hatırlamak önemlidir ( jQuery dokümanlarından ):

JQuery 1.4.x ve önceki sürümlerinde, istek zaman aşımına uğradığında XMLHttpRequest nesnesi geçersiz bir durumda olacaktır; herhangi bir nesne üyesine erişim bir istisna oluşturabilir. Yalnızca Firefox 3.0 ve sonraki sürümlerinde, komut dosyası ve JSONP istekleri zaman aşımı ile iptal edilemez; komut dosyası zaman aşımı süresinden sonra gelse bile çalışır.

Uzun Çağırma - Sunucu

Belirli bir dilde değil, ancak şöyle bir şey olurdu:

function handleRequest () {  

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

     return events();

} 

Burada, hasTimedOutkodunuzun sonsuza kadar beklemediğinden emin olacak ve anythingHappenedherhangi bir olayın olup olmadığını kontrol edecektir. sleepHiçbir şey olmuyor iken başka şeyler yapmak için iplik salan içindir. eventsJSON biçiminde olayların bir sözlüğü (veya tercih herhangi bir başka veri yapısını) döndürecektir (veya başka tercih).

Sorunu kesinlikle çözer, ancak araştırırken olduğu gibi ölçeklenebilirlik ve performans konusunda endişeleriniz varsa, bulduğum başka bir çözümü düşünebilirsiniz.

Çözüm

Yuva kullanın!

İstemci tarafında, uyumluluk sorunlarından kaçınmak için socket.io kullanın . Soketi doğrudan kullanmaya çalışır ve soketler olmadığında diğer çözümlere geri dönüşler yapar.

Sunucu tarafında, NodeJS kullanarak bir sunucu oluşturun (örnek burada ). İstemci, sunucuyla oluşturulan bu kanala (gözlemci) abone olur. Bir bildirim gönderilmesi gerektiğinde, bu kanalda yayınlanır ve aboneye (istemci) bildirim gönderilir.

Bu çözümü beğenmediyseniz, APE'yi ( Ajax Push Engine ) deneyin .

Umarım yardımcı oldum.


1'in diğerinin yerini aldığını veya aynı projede her iki teknolojiye de ihtiyaç olduğunu düşünüyor musunuz?
tq

APE ve NodeJS'yi kastediyorsanız, bunlardan birini seçebilirsiniz. periyodik AJAX istekleri ve önerdiğim şeyleri kastediyorsanız, soket desteğinden yoksun olduğunda çözümüm ajax'a geri dönebilir (bkz. socket.io docs). Her iki durumda da, sadece bir çözüme ihtiyacınız vardır.
Walter Macambira

Merhaba Walter, önerinizi sitelerimden birinde kullanmak istiyorum. Bir Sockets sunucusunu nereden bulabileceğimi biliyor musunuz? Teşekkürler!
Progo

1
Uygulayabilirsiniz. Düğüm gerçekten çok basit.
Walter Macambira

Nasıl tespit edilir hasTimedOut()?
Mobasher Fasihy

18

Facebook'un Mesajlaşma sistemi ile ilgili bir slayt gösterisine göre Facebook, web tarayıcılarına mesajı "itmek" için kuyruklu yıldız teknolojisini kullanıyor. Facebook kuyruklu yıldız sunucusu açık kaynaklı Erlang web sunucusu mochiweb üzerine kurulmuştur.

Aşağıdaki resimde, "kanal kümeleri" ifadesi "kuyruklu yıldız sunucuları" anlamına gelir.

Sistem görünümü

Diğer birçok büyük web sitesi kendi kuyruklu yıldız sunucusunu oluşturur, çünkü her şirketin ihtiyacı arasında farklılıklar vardır. Ancak açık kaynak kodlu bir kuyruklu sunucu üzerinde kendi kuyruklu yıldız sunucunuzu oluşturmak iyi bir yaklaşımdır.

Deneyebilirsin icomet , Libevent ile inşa edilmiş bir C1000K C ++ Comet sunucu. icomet ayrıca bir JavaScript kütüphanesi sağlar, kullanımı basittir:

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'ler (Windows), Firefox, Chrome vb. dahil çok çeşitli Tarayıcıları ve İşletim Sistemlerini destekler.


Bu görüntü senaryoyu çok iyi açıklıyor. Bir eylem örneği verilmiş olsaydı harika olurdu. Örneğin, bir kişi bir arkadaşıyla bir sohbet kutusunu açtığında (başlattığında) ne olur? Facebook bu belirli konuşmayı nasıl ayarlar ve mesajları her iki tarafa da iter? (sadece bir tahmin: Sadece uygulama programının bir soket açtığını ve her iki istemci adresini de bağladığını ve kutuya her mesaj yazıldığında dinlemeye ve yazmaya devam edebildiğini hayal edebiliyorum)
edam

5

Facebook, HTTP yerine MQTT kullanıyor. İtme yoklamadan daha iyidir. HTTP üzerinden sunucuyu sürekli yoklamamız gerekir, ancak MQTT sunucusu aracılığıyla iletiyi istemcilere iletir.

MQTT ve HTTP karşılaştırması: http://www.youtube.com/watch?v=-KNPXPmx88E

Not: Yanıtlarım mobil cihazlara en uygun.


3
Ayrıca, google android için GCM hizmetini kullanır, geliştiriciler tarafından push mesajı hizmetini uygulamak için kullanılabilir. developer.android.com/google/gcm/index.html Cevabı yararlı buluyorsanız lütfen kabul edin.
abhi

5

Uzun yoklama ile ilgili önemli bir sorun hata işlemedir. İki tür hata vardır:

  1. İstek zaman aşımına uğrayabilir, bu durumda istemci bağlantıyı hemen yeniden kurmalıdır. Hiçbir mesaj gelmediğinde uzun oylamada normal bir olaydır.

  2. Bir ağ hatası veya bir yürütme hatası. Bu, istemcinin nazikçe kabul etmesi ve sunucunun tekrar çevrimiçi olmasını beklemesi gereken gerçek bir hatadır.

Asıl sorun, hata işleyiciniz bağlantıyı hemen bir tür 2 hatası için de yeniden kurarsa, istemcilerin sunucuyu DOS kullanmasıdır.

Kod örneği ile her iki cevap da bunu kaçırıyor.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.