Серверні події та php - що запускає події на сервері?


91

Всі,

HTML5 Rocks має приємний підручник для початківців, що надсилаються сервером (SSE):

http://www.html5rocks.com/en/tutorials/eventsource/basics/

Але я не розумію важливої ​​концепції - що викликає подію на сервері, яка спричиняє надсилання повідомлення?

Іншими словами - у прикладі HTML5 - сервер просто надсилає позначку часу один раз :

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));

Якби я будував практичний приклад - наприклад, "стіну" у стилі Facebook або фондовий білет, в якому сервер "штовхав" нове повідомлення клієнту щоразу, коли якась частина даних змінюється, як це працює?

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

Або - сценарій PHP просто надсилає повідомлення, а потім закінчує (як це трапляється у прикладі HTML5Rocks)? Якщо так - як ви отримуєте постійні оновлення? Чи браузер просто опитує сторінку PHP через рівні проміжки часу? Якщо так, то як це "передана сервером подія"? Чим це відрізняється від написання функції setInterval в JavaScript, яка використовує AJAX для виклику PHP-сторінки з регулярним інтервалом?

Вибачте - це, мабуть, неймовірно наївне питання. Але жоден із прикладів, які мені не вдалося знайти, не дає зрозуміти цього.

[ОНОВЛЕННЯ]

Я думаю, що моє запитання було сформульоване погано, тож тут є кілька роз’яснень.

Скажімо, у мене є веб-сторінка, яка повинна відображати найсвіжішу ціну акцій Apple.

Коли користувач вперше відкриває сторінку, сторінка створює EventSource з URL-адресою мого "потоку".

var source = new EventSource('stream.php');

Моє питання полягає в наступному - як повинен працювати "stream.php"?

Подобається це? (псевдокод):

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
    function sendMsg($msg) {
        echo "data: $msg" . PHP_EOL;
        echo PHP_EOL;
        flush();
    }

    while (some condition) {
        // check whether Apple's stock price has changed
        // e.g., by querying a database, or calling a web service
        // if it HAS changed, sendMsg with new price to client
        // otherwise, do nothing (until next loop)
        sleep (n) // wait n seconds until checking again
    }
?>

Іншими словами - чи «stream.php» залишається відкритим до тих пір, поки клієнт «підключений» до нього?

Якщо так - чи означає це, що у вас працює стільки потоків, stream.phpскільки у вас одночасних користувачів? Якщо так - чи можливо це віддалено, або відповідний спосіб створення програми? І звідки ви знаєте, коли ви можете ЗАКОНЧИТИ екземпляр stream.php?

Моє наївне враження полягає в тому, що, якщо це так, PHP не є придатною технологією для такого роду серверів. Але всі демонстрації, які я бачив до цього часу, свідчать про те, що PHP просто чудово підходить для цього, ось чому я так розгублений ...


Це та частина, яку розробник повинен кодувати самостійно. Засоби отримання даних здійснюються через веб-сокети / довге опитування тощо, однак фокус полягає в тому, що ініціює подію. Особисто я експериментував з декількома підходами і один підхід , який мені сподобався (але це не було , що відмовостійкий) робив MySQL тригер консольна програма кожен раз , коли що - то було вставлено в конкретної таблиці. Консольна програма отримає інформацію про змінений / вставлений запис і надсилатиме повідомлення відповідному користувачеві через WebSockets. В основному у мене був демон PHP, який чекав відправки повідомлень.
NB

Одна проблема з цим, SSE не підтримується IE: - / Також я б прочитав це prodigyproductionsllc.com/articles/programming/javascript/ ... Я думаю, що він використовує порт, щоб уникнути проблеми занадто багато дітей, але загалом схоже на його Рекомендація полягає в тому, щоб уникати SSE. Виглядає набагато більше клопоту, ніж це варто, IMO.
PJ Brunet

На даний момент не підтримується IE11 або браузером Android caniuse.com/eventsource
PJ Brunet

1
Якщо комусь потрібен PHP-код sse: github.com/shahzadthathal/server-sent-events-php-example
Мухаммад Шахзад

4
У мене був один і той же питання , і я думаю , що я глибоко розумію , що ви маєте в виду , що викликає подія на сервері ... . Коли ви створюєте об'єкт EventSource('stream.php'), клієнт відкриває з'єднання, stream.phpяке схоже на виклик його ajax. ЦЕ підключення запускає ваш код на стороні сервера і тримає зв’язок відкритим, доки код на стороні сервера має щось сказати. Потім з'єднання замикається, і через невелику затримку (я думаю, що хром - 3 секунди) клієнт знову відкриває з'єднання, яке stream.phpзнову запускає ваш файл.
Ахмад Малекі,

Відповіді:


28

"... чи" stream.php "залишається відкритим до тих пір, поки клієнт" підключений "до нього?"

Так, і ваш псевдокод - це розумний підхід.

"А звідки ви знаєте, коли можна ЗАКІНЧИТИ екземпляр stream.php?"

У найбільш типовому випадку це відбувається, коли користувач залишає ваш сайт. (Apache розпізнає закритий сокет і вбиває екземпляр PHP.) Основний час, коли ви можете закрити сокет з боку сервера, це якщо ви знаєте, що деякий час даних не буде; останнє повідомлення, яке ви надсилаєте клієнту, - це сказати йому повернутися в певний час. Наприклад, у вашому випадку з потоковою передачею акцій ви можете перервати з'єднання о 20:00 та сказати клієнтам повернутися через 8 годин (за умови, що NASDAQ відкритий для котирувань з 4 ранку до 8 вечора). У п’ятницю ввечері ви скажете їм повернутися в понеділок вранці. (У мене є майбутня книга про SSE, і я присвячую кілька розділів на цю тему.)

"... якщо це так, PHP не є підходящою технологією для такого роду серверів. Але всі демонстраційні демонстрації, які я бачив до цього часу, свідчать про те, що PHP просто чудово підходить для цього, саме тому я так розгублений ... "

Ну, люди стверджують, що PHP не є придатною технологією для звичайних веб-сайтів, і вони мають рацію: ви могли б зробити це з набагато меншою кількістю циклів пам'яті та процесора, якби замінити весь стек LAMP на C ++. Однак, незважаючи на це, PHP дуже добре керує більшістю веб-сайтів. Це дуже продуктивна мова для роботи в Інтернеті завдяки поєднанню знайомого Си-подібного синтаксису та великої кількості бібліотек, а також втішного для менеджерів, оскільки багато PHP-програмістів найняти, багато книг та інших ресурсів, а також деякі великі варіанти використання (наприклад, Facebook та Wikipedia). Це в основному ті самі причини, чому ви можете вибрати PHP як свою потокову технологію.

Типовим налаштуванням не буде одне підключення до NASDAQ для кожного екземпляра PHP. Натомість у вас буде інший процес з одним підключенням до NASDAQ або, можливо, єдиним підключенням від кожної машини у вашому кластері до NASDAQ. Це потім штовхає ціни або на сервер SQL / NoSQL, або на спільну пам’ять. Тоді PHP просто опитує спільну пам'ять (або базу даних) і витісняє дані. Або мати сервер збору даних, і кожен екземпляр PHP відкриває сокетне з’єднання з цим сервером. Сервер збору даних виштовхує оновлення для кожного свого клієнта PHP, коли він отримує їх, а вони, в свою чергу, передають ці дані своєму клієнту.

Основною проблемою масштабованості використання Apache + PHP для потокового передавання є пам'ять для кожного процесу Apache. Коли ви досягнете межі пам'яті апаратного забезпечення, прийміть ділове рішення про додавання ще однієї машини до кластера або виріжте Apache з циклу та напишіть виділений HTTP-сервер. Останнє можна зробити в PHP, щоб усі ваші знання та код могли бути використані повторно, або ви можете переписати весь додаток іншою мовою. Чистий розробник у мене написав би виділений, спрощений HTTP-сервер на C ++. Менеджер в мене додав би ще одне поле.


Оскільки кожен процес підключення Apache споживає пам’ять, чи буде краще замість цього використовувати Nginx?
Zhang Buzz

@ZhangBuzz Там, де я вже сказав Apache + PHP, це насправді означає "веб-сервер + процес PHP", тому в основному немає різниці з використанням іншого веб-сервера.
Даррен Кук

Може, такі сервери? github.com/hoaproject/Eventsource або github.com/hhxsv5/php-sse
Енріке

Також зверніть увагу, що Nginx може бути набагато ефективнішим для цього, оскільки використовує менше пам'яті: blog.webfaction.com/2008/12/…
Енріке,

31

Події, що надсилаються сервером, призначені для оновлення в режимі реального часу з боку сервера на сторону клієнта. У першому прикладі з'єднання з сервером не зберігається, і клієнт намагається підключатися знову кожні 3 секунди і не робить події, що надсилаються сервером, різниці до опитування ajax.

Отже, щоб зв’язок не зникав, вам потрібно загорнути код у цикл і постійно перевіряти наявність оновлень.

PHP базується на потоках, і більше підключених користувачів призведе до того, що на сервері закінчаться ресурси. Це можна вирішити, контролюючи час виконання сценарію та закінчуючи сценарій, коли він перевищує певний проміжок часу (тобто 10 хвилин). EventSourceAPI , буде автоматично підключатися знову , так що затримка в прийнятному діапазоні.

Крім того, перегляньте мою бібліотеку PHP щодо подій , що надсилаються сервером , ви можете зрозуміти більше про те, як робити події, що надсилаються сервером у PHP, і спростити кодування.


5
Не могли б ви детальніше сказати "Це можна вирішити, контролюючи час виконання сценарію та закінчуючи сценарій, коли це перевищує час"? Якщо у вас надмірна кількість користувачів, чи справді це значно покращить використання ресурсів, закривши з'єднання, оскільки користувач просто підключиться знову за 3 секунди?
Лука

4

Я помітив, що sse techink надсилає клієнту кожну пару даних про затримку (щось на кшталт скасування звороту techink даних пулу з клієнтської сторінки, коли Ajax об'єднує дані.), Щоб подолати цю проблему, я зробив це на сторінці sseServer.php:

<?php
        session_start();
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache'); // recommended to prevent caching of event data
        require 'sse.php';
        if ($_POST['message'] != ""){
                $_SESSION['message'] = $_POST['message'];
                $_SESSION['serverTime'] = time();
        }
        sendMsg($_SESSION['serverTime'], $_SESSION['message'] );
?>

а sse.php:

<?php
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
?>

Зверніть увагу, що на sseSerer.php я розпочинаю сеанс із використанням змінної сеансу! подолати проблему.

Також я закликаю sseServer.php через Ajax (розміщення і встановлення значення variable message) кожного разу, коли я хочу "оновити" повідомлення.

Тепер у jQuery (javascript) я роблю щось подібне: 1-е) я оголошую глобальну змінну var timeStamp = 0; 2-е) я використовую наступний алгоритм:

if(typeof(EventSource)!=="undefined"){
        var source=new EventSource("sseServer.php");
        source.onmessage=function(event)
        if ((timeStamp!=event.lastEventId) && (timeStamp!=0)){
                /* this is initialization */
                timeStamp=event.lastEventId;
                $.notify("Please refresh "+event.data, "info");
        } else {
                if (timeStamp==0){
                         timeStamp=event.lastEventId;
                }
        } /* fi */

} else {
        document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
} /* fi */

У рядку: $.notify("Please refresh "+event.data, "info"); чи є ви можете обробити повідомлення.

У моєму випадку я надсилав повідомлення jQuery.

Ви можете замість цього використовувати POSIX PIPES або таблицю DB для передачі "повідомлення" через POST, оскільки sseServer.php робить щось на зразок "нескінченного циклу".

Моя проблема на той момент полягає в тому, що наведений вище код НЕ НАДІСЛЯЄ "повідомлення" всім клієнтам, а лише парі (клієнт, який називається sseServer.php, працює як індивідуальний для кожної пари), тому я зміню техніку та Оновлення БД зі сторінки, яку я хочу викликати "повідомлення", а потім sseServer.php замість цього, щоб отримати повідомлення через POST, воно отримає його з таблиці БД.

Я сподіваюся, що у мене є допомога!


3

Це справді структурне запитання щодо вашої заявки. Події в реальному часі - це те, про що ви хочете подумати з самого початку, тому ви можете розробити свою програму навколо неї. Якщо ви написали програму, яка просто запускає купу випадкових mysql(i)_queryметодів із використанням рядкових запитів і не передає їх через будь-який посередник, то багато разів у вас не буде іншого вибору, окрім як переписати більшу частину своєї програми, або постійне опитування на стороні сервера.

Однак, якщо ви керуєте своїми об'єктами як об'єктами і передаєте їх через якийсь посередницький клас, ви можете підключитись до цього процесу. Подивіться на цей приклад:

<?php
class MyQueryManager {
    public function find($myObject, $objectId) {
        // Issue a select query against the database to get this object
    }

    public function save($myObject) {
        // Issue a query that saves the object to the database
        // Fire a new "save" event for the type of object passed to this method
    }

    public function delete($myObject) {
        // Fire a "delete" event for the type of object
    }
}

Коли ви готові до збереження, у своїй програмі:

<?php
$someObject = $queryManager->find("MyObjectName", 1);
$someObject->setDateTimeUpdated(time());
$queryManager->save($someObject);

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

Ви, очевидно, не зможете вловити зміни в базі даних вручну, але якщо ви щось робите вручну у своїй базі даних із будь-якою частотою, вам слід:

  • Вирішіть проблему, яка вимагає ручної зміни
  • Створіть інструмент для прискорення процесу та запустіть ці події

3
Колін - дякую за вашу відповідь. Моя провина - моє запитання незрозуміле - але це насправді не те, про що я запитую. Я хотів запитати це ... Якщо ви використовуєте PHP як "сервер" - чи повинен PHP-скрипт, який ви викликаєте з EventSource у своєму клієнті, запускатися весь час, коли клієнт підключений до нього? Чи означає це, що якщо у вас 1000 одночасних користувачів, у вас є 1000 окремих потоків, що запускають 1000 одночасних екземплярів вашого PHP-сценарію? Це можливо? І, як би ви знали, коли закінчувати php-скрипт (припускаючи, що цикл залишається «живим»)?
mattstuehler

-7

В основному, PHP не підходить для такої технології. Так, ви можете змусити це працювати, але при великих навантаженнях це буде катастрофою. Ми запускаємо біржові сервери, які надсилають сигнали про зміну запасів через веб-сокети десяткам тисяч користувачів - і якби ми використовували для цього php ... Ну, ми могли б, але ці домашні цикли - це просто кошмар. Кожне окреме з'єднання буде робити окремий процес на сервері, або вам доведеться обробляти з'єднання з якоїсь бази даних.

Просто використовуйте nodejs і socket.io. Це дозволить вам легко запустити і мати запущений сервер за кілька днів. Nodejs також має власні обмеження, але для веб-сокетів (і SSE) зараз це найпотужніша технологія.

А також - SSE не така вже й гарна, як здається. Єдина перевага веб-сокетів - це те, що пакети кодуються в стилі gzipped (ws не gzipped), але недоліком є ​​те, що SSE є одностороннім підключенням. Користувач, якщо він хоче додати ще один символ акцій до абонементу, повинен буде зробити запит ajax (включаючи всі проблеми з контролем джерела, і запит буде повільним). У Websockets client і sever обмінюються даними обома способами одним відкритим з'єднанням, тому, якщо користувач надсилає торговий сигнал або підписується на котирування, він просто відправляє рядок у вже відкрите з'єднання. І це швидко.


2
Ви можете використовувати React.php в основному так само, як цикл подій у node.js.
Матей Кубік

3
Хоча добре сказати, що PHP не є найкращим вибором, я думаю, що вам слід принаймні включити щось, про що запитував OP.
Nishchal Gautam
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.