Отправленные сервером события и php - что запускает события на сервере?


93

Все,

В 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. Похоже, проблем больше, чем того стоит, ИМО.
PJ Brunet

В настоящее время не поддерживается браузером IE11 или Android caniuse.com/eventsource
PJ Brunet


4
У меня был один и тот же вопрос , и я думаю , что я глубоко понимаю , что вы имеете в виду , что вызывает событие на сервере ... . Когда вы создаете объект EventSource('stream.php'), клиент открывает соединение, stream.phpкоторое аналогично его вызову с помощью ajax. ЭТО соединение запускает ваш серверный код и сохраняет соединение открытым до тех пор, пока вашему серверному коду есть что сказать. Затем соединение закрывается, и после небольшой задержки (кажется, 3 секунды в chrome) клиент повторно открывает соединение, которое stream.phpснова запускает ваш файл.
Ахмад Малеки

Ответы:


29

"..." stream.php "остается открытым, пока клиент" подключен "к нему?"

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

«А как узнать, можно ли закончить экземпляр stream.php?»

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

"... если это так, PHP не подходит для такого типа серверов. Но все демонстрации, которые я видел до сих пор, подразумевают, что PHP подходит для этого, поэтому я так смущенный..."

Что ж, люди утверждают, что PHP не подходит для обычных веб-сайтов, и они правы: вы могли бы сделать это с гораздо меньшими циклами памяти и процессора, если бы вы заменили весь свой стек LAMP на C ++. Однако, несмотря на это, PHP работает на большинстве сайтов нормально. Это очень продуктивный язык для веб-работы благодаря сочетанию знакомого синтаксиса, подобного C, и большого количества библиотек, а также удобный язык для менеджеров, так как множество программистов PHP, которых можно нанять, множество книг и других ресурсов, а также несколько больших варианты использования (например, Facebook и Википедия). По сути, это те же причины, по которым вы можете выбрать 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/…
Enrique

32

События, отправленные сервером, предназначены для обновления в реальном времени со стороны сервера на сторону клиента. В первом примере соединение с сервером не сохраняется, и клиент пытается подключиться снова каждые 3 секунды, и события, отправленные сервером, не отличаются от опроса ajax.

Итак, чтобы соединение сохранялось, вам нужно обернуть свой код в цикл и постоянно проверять наличие обновлений.

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

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


5
Не могли бы вы уточнить: «Эту проблему можно решить, контролируя время выполнения скрипта и завершая скрипт, когда оно превышает заданное время»? Если у вас чрезмерное количество пользователей, действительно ли это значительно улучшит использование ресурсов, закрыв соединение, поскольку пользователь просто подключится снова через 3 секунды?
Люк

4

Я заметил, что sse techink отправляет каждую пару данных о задержке клиенту (что-то вроде реверсирования пула данных techink со страницы клиента ex Ajax pooling data.), Поэтому для решения этой проблемы я сделал это на странице 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 или таблицу БД вместо того, чтобы передать «сообщение» через 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 не так хорош, как кажется. Единственное преимущество веб-сокетов - это то, что пакеты архивируются изначально (ws не архивируются), но с другой стороны, SSE - это одностороннее соединение. Пользователь, если он хочет добавить еще один символ акции в подписку, должен будет сделать запрос ajax (включая все проблемы с контролем происхождения, и запрос будет медленным). В веб-сокетах клиент и сервер обмениваются данными в обоих направлениях в одном открытом соединении, поэтому, если пользователь отправляет торговый сигнал или подписывается на котировку, он просто отправляет строку в уже открытом соединении. И это быстро.


2
Вы можете использовать React.php в основном так же, как цикл событий в node.js.
Matěj Koubík 07

3
Хотя хорошо сказать, что PHP - не лучший выбор, я думаю, вы должны хотя бы включить то, о чем просил OP.
Нищал Гаутам 01
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.