Все,
В 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 // eg, 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
экземпляр stream.php
?
Мое наивное впечатление заключается в том, что, если это так, PHP не является подходящей технологией для такого рода серверов. Но все демонстрации, которые я видел до сих пор, подразумевают, что PHP подходит для этого, поэтому я так запутался …
Заранее спасибо.
События, отправленные сервером, предназначены для обновления в реальном времени с серверной стороны на стороне клиента. В первом примере соединение с сервером не поддерживается, и клиент пытается подключиться снова каждые 3 секунды и делает события, отправленные сервером, без разницы в ajax-опросе.
Таким образом, чтобы сохранить соединение, вам необходимо обернуть свой код в цикле и постоянно проверять наличие обновлений.
PHP основан на потоках, и более подключенные пользователи заставят сервер работать без ресурсов. Это можно решить, контролируя время выполнения скрипта и заканчивая сценарий, когда он превышает количество времени (т.е. 10 минут). API EventSource
автоматически подключится снова, чтобы задержка была в допустимом диапазоне.
Кроме того, ознакомьтесь со своей библиотекой PHP для событий , отправленных сервером , вы можете больше узнать о том, как делать серверные события на PHP и упрощать их кодирование.
«…« stream.php »остается открытым, пока клиент« подключен »к нему?»
Да, и ваш псевдокод – разумный подход.
«И откуда вы знаете, когда вы можете END экземпляр stream.php?»
В наиболее типичном случае это происходит, когда пользователь покидает ваш сайт. (Apache распознает закрытый сокет и убивает экземпляр PHP.) Основной момент, когда вы можете закрыть сокет с серверной стороны, – это если вы знаете, что на какое-то время не будет данных; последнее сообщение, которое вы отправляете клиенту, – это сказать им вернуться в определенное время. Например, в вашем потоке с запасом можно закрыть соединение в 8 вечера и сообщить клиентам вернуться через 8 часов (при условии, что NASDAQ открыт для котировок с 4:00 до 20:00). В пятницу вечером вы скажите им вернуться в понедельник утром. (У меня есть предстоящая книга по SSE, и посвятите пару разделов по этому вопросу.)
«… если это так, PHP не является подходящей технологией для такого типа серверов. Но все демонстрации, которые я видел до сих пор, подразумевают, что PHP подходит для этого, и именно поэтому я так смущенный…"
Ну, люди утверждают, что PHP не является подходящей технологией для обычных веб-сайтов, и они правы: вы могли бы сделать это с гораздо меньшим количеством циклов памяти и процессора, если бы вы заменили весь ваш стек LAMP на C ++. Однако, несмотря на это, PHP предоставляет большинство сайтов там просто отлично. Это очень продуктивный язык для веб-работы, благодаря сочетанию знакомого синтаксиса C-like и так много библиотек, а также удобному для менеджеров, поскольку многие программисты PHP нанимают, много книг и других ресурсов, а также некоторые крупные (например, Facebook и Википедия). В основном это те же причины, по которым вы можете выбрать PHP как свою технологию потоковой передачи.
Типичная настройка не будет одним подключением к NASDAQ для PHP-экземпляра. Вместо этого у вас будет другой процесс с единственным подключением к NASDAQ или, возможно, с одним соединением с каждого компьютера в вашем кластере на NASDAQ. Это затем подталкивает цены на сервер SQL / NoSQL или в общую память. Затем PHP просто проверяет эту общую память (или базу данных) и выталкивает данные. Или, у вас есть сервер сбора данных, и каждый экземпляр PHP открывает сокет-соединение с этим сервером. Сервер сбора данных выталкивает обновления каждому из своих PHP-клиентов, поскольку он их получает, и они, в свою очередь, выталкивают эти данные своему клиенту.
Основная проблема масштабируемости при использовании Apache + PHP для потоковой передачи – это память для каждого процесса Apache. Когда вы достигнете предела памяти аппаратного обеспечения, сделайте бизнес-решение о добавлении другого компьютера в кластер или вырезайте Apache из цикла и напишите выделенный HTTP-сервер. Последнее можно сделать на PHP, чтобы все ваши существующие знания и код можно было повторно использовать, или вы можете переписать все приложение на другом языке. Чистый разработчик во мне напишет выделенный, оптимизированный HTTP-сервер в C ++. Менеджер во мне добавил бы еще одну коробку.
Я заметил, что sse techink отправляет каждую пару данных задержки клиенту (что-то вроде того, что реверсирует сбор данных данных из клиентской страницы ex 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) я делаю что-то вроде этого: 1st) i объявляет глобальную переменную var timeStamp = 0; 2nd) я использую следующий алгоритм:
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, он получит его из таблицы БД.
Надеюсь, что у меня есть помощь!
Это действительно структурный вопрос о вашем приложении. События в реальном времени – это то, о чем вы хотите подумать с самого начала, чтобы вы могли проектировать свое приложение вокруг него. Если вы написали приложение, которое просто запускает кучу случайных методов 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);
Это не самый изящный пример, но он должен служить достойным строительным блоком. Вы можете подключиться к вашему фактическому уровню сохранения, чтобы обрабатывать эти события. Затем вы получаете их сразу (как в режиме реального времени, как это может получиться), не забивая ваш сервер (так как вам не нужно постоянно запрашивать вашу базу данных и видеть, изменились ли какие-либо вещи).
Очевидно, что вы не поймаете ручные изменения в базе данных, но если вы делаете что-либо вручную в своей базе данных с любой частотой, вы должны либо:
В принципе, PHP не подходит технологии для такого рода вещей. Да, вы можете заставить его работать, но это будет катастрофа на высокой нагрузке. Мы запускаем агенты-хранилища, которые отправляют сигналы изменения запасов через веб-порты до десятков тысяч пользователей – и если мы будем использовать php для этого … Ну, мы могли бы, но эти домашние циклы – это просто кошмар. Каждое отдельное соединение будет выполнять отдельный процесс на сервере или вам придется обрабатывать соединения из какой-либо базы данных.
Просто используйте nodejs и socket.io. Это позволит вам легко запустить и запустить запущенный сервер через пару дней. У Nodejs также есть свои ограничения, но для соединений с websockets (и SSE) теперь является самой мощной технологией.
А также – SSE не так хорош, как кажется. Единственное преимущество для websockets – это то, что пакеты gzipped изначально (ws не gzipped), но с другой стороны, SSE является односторонним соединением. Пользователь, если он хочет добавить еще один символ запаса в подкодер, должен будет сделать запрос ajax (включая все проблемы с контролем происхождения и запрос будет медленным). В websockets клиент и сервер обмениваются обоими путями в одном открытом открывшемся соединении, поэтому, если пользователь отправляет торговый сигнал или подписывается на котировку, он просто отправляет строку в уже открытое соединение. И это быстро.