Як facebook, gmail надсилає сповіщення в режимі реального часу?


269

Я прочитав кілька публікацій на цю тему, і відповіді - комета, зворотний аякс, потокова http, натискання сервера тощо.

Як працює повідомлення про вхідну пошту в Gmail?

Як GMail Chat може робити запити AJAX без взаємодії з клієнтом?

Мені хотілося б знати, чи є якісь посилання на код, які я можу дотримуватися, щоб написати дуже простий приклад. Багато публікацій або веб-сайтів просто говорять про технологію. Важко знайти повний зразок коду. Крім того, здається, що для реалізації комети може використовуватися багато методів, наприклад, прихована IFrame, XMLHttpRequest. На мою думку, використання XMLHttpRequest є кращим вибором. Що ви думаєте про плюси і мінуси різних методів? Який з них використовується Gmail?

Я знаю, що це потрібно робити як на стороні сервера, так і на стороні клієнта. Чи є зразок коду PHP та Javascript?

Відповіді:


428

Те, як це робить Facebook, є досить цікавим.

Поширений метод здійснення таких сповіщень - опитування скрипту на сервері (за допомогою AJAX) на заданий інтервал (можливо, кожні кілька секунд), щоб перевірити, чи щось сталося. Однак це може бути досить інтенсивно в мережі, і ви часто робите безглузді запити, тому що нічого не сталося.

Те, як Facebook робить це, використовує кометний підхід, а не опитування з інтервалом, як тільки одне опитування завершиться, воно видає ще одне. Однак кожен запит до сценарію на сервері має надзвичайно тривалий час, і сервер відповідає на запит лише після того, як щось трапилося. Ви можете побачити це, якщо ви перейдете на вкладку "Консоль Firebug", перебуваючи у Facebook, із запитами до сценарію, можливо, потрібно хвилин. Це справді досить геніально, оскільки цей метод негайно скорочує як кількість запитів, так і те, як часто вам доведеться їх надсилати. Ви фактично маєте рамку подій, яка дозволяє серверу «запускати» події.

За цим, з точки зору фактичного вмісту, поверненого з цих опитувань, стоїть відповідь JSON, в якій описується список подій та інформація про них. Це все ж мінімізоване, тому читати трохи важко.

З точки зору реальної технології, AJAX - це шлях, який можна перейти сюди, оскільки ви можете керувати таймаутами запитів та багатьма іншими речами. Я рекомендую (Стек тут переповнюйте стек) за допомогою jQuery робити AJAX, це займе багато проблем з перехресною сумісністю. Щодо PHP, ви можете просто опитати таблицю бази даних журналу подій у вашому PHP-скрипті та повертатися до клієнта лише тоді, коли щось відбувається? Сподіваюсь, існує багато способів здійснення цього.

Реалізація:

Сторона сервера:

Здається, що в PHP є кілька реалізацій бібліотек комет, але якщо чесно, це дуже просто, щось, на зразок наступного псевдокоду:

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

echo json_encode(get_events());
  • Функція has_event_happened просто перевірить, чи щось трапилось у таблиці подій чи щось таке, а потім функція get_events поверне список нових рядків у таблиці? Справді залежить від контексту проблеми.

  • Не забудьте змінити максимальний термін виконання PHP, інакше він закінчиться достроково!

Сторона клієнта:

Погляньте на плагін jQuery для взаємодії з Comet:

Однак, плагін, здається, додає неабиякої складності, він дійсно дуже простий для клієнта, можливо (з 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
Це дуже приємне і детальне пояснення. Дякую. Чи є у вас зразок коду для одного з багатьох способів реалізації цього?
Біллі

45
Я думаю, що маркування PHP як мови / платформи, яка не має масштабів, не обов'язково відповідає дійсності. Його можна використовувати для розробки надзвичайно масштабних систем. Подивіться у facebook. Якщо розробник зробить це правильно, то він буде масштабувати, якщо ні, то не буде. Використання певної веб-платформи не є гарантією масштабованості. О, і також, це питання задав PHP.
Алістер Еванс

5
@Kazar: "Facebook використовує PHP" трохи вводить в оману - останнє, що я чув, вони розробили HipHop для прямої мети перетворення PHP на C ++, оскільки PHP працював недостатньо добре.
cHao

14
@cHao: Справедливий момент, проте ця відповідь була написана в 2009 році, перш ніж у Facebook почали використовувати хіпхоп. У той час у Facebook все ще була дуже масштабна система, що використовує php самостійно.
Алістер Еванс

6
Таким чином, техніка полягає в тому, щоб постійно тримати зв’язок відкритим, який буде тримати сервер у постійній напрузі. Типова кількість одночасних з'єднань для середнього веб-сервера - близько 200, але кількість користувачів Facebook, які одночасно перебувають у мережі, набагато більша. Як вони це роблять?
Павло

43

Оновлення

Коли я продовжую отримувати відгуки щодо цього, я вважаю, що доцільно пам'ятати, що ця відповідь становить 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 ):

У 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 (приклад тут ). Клієнт підписується на цей канал (спостерігач), створений із сервером. Щоразу, коли повідомлення повинно бути надіслане, воно публікується на цьому каналі, і підписник (клієнт) отримує повідомлення.

Якщо це рішення вам не подобається, спробуйте APE ( Ajax Push Engine ).

Сподіваюся, я допоміг.


чи вважаєте ви, що 1 є заміною для іншого чи є потреба в обох технологіях одного і того ж проекту?
tq

Якщо ви маєте на увазі APE та NodeJS, ви можете вибрати один з них. якщо ви маєте на увазі періодичні запити AJAX і той, який я запропонував, моє рішення може відпадати до ajax, коли не вистачає підтримки socket (див. документи socket.io). В обох випадках потрібно лише одне рішення.
Вальтер Макамбіра

Ей, Уолтере, я хотів би використати вашу пропозицію на одному з моїх сайтів. Чи знаєте ви, де я можу отримати сервер Sockets? Дякую!
Прого

1
Ви можете його реалізувати. Вузол робить його справді простим.
Вальтер Макамбіра

Як виявити hasTimedOut()?
Мобашер Фасіхій

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

icomet підтримує широкий спектр браузерів і ОС, включаючи Safari (iOS, Mac), IE (Windows), Firefox, Chrome тощо.


Це зображення дуже добре описує сценарій. Було б чудово, якби наводився приклад дії. Наприклад, що відбувається, коли людина відкриває (ініціює) спілкування в чаті з другом? Як facebook налаштовується на цю конкретну розмову і підштовхує повідомлення до обох кінців? (лише здогадка: я можу лише уявити, що прикладна програма відкриває сокет і прив’язує обидві адреси клієнта, а потім просто продовжуйте слухати та писати, коли повідомлення буде записане у вікні)
edam

5

Facebook використовує MQTT замість HTTP. Push краще, ніж опитування. Через HTTP нам потрібно постійно опитувати сервер, але через MQTT сервер передає повідомлення клієнтам.

Порівняння MQTT і HTTP: http://www.youtube.com/watch?v=-KNPXPmx88E

Примітка: мої відповіді найкраще відповідають мобільним пристроям.


3
Крім того, Google використовує службу GCM для android, вона може бути використана розробниками для впровадження послуги push-повідомлень. developer.android.com/google/gcm/index.html Будь ласка, приймайте, якщо вважаєте відповідь корисною.
abhi

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.