Все,
В 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 отлично подходит для этого, поэтому я так смущен ...
источник
EventSource('stream.php')
, клиент открывает соединение,stream.php
которое аналогично его вызову с помощью ajax. ЭТО соединение запускает ваш серверный код и сохраняет соединение открытым до тех пор, пока вашему серверному коду есть что сказать. Затем соединение закрывается, и после небольшой задержки (кажется, 3 секунды в chrome) клиент повторно открывает соединение, котороеstream.php
снова запускает ваш файл.Ответы:
Да, и ваш псевдокод - разумный подход.
В наиболее типичном случае это происходит, когда пользователь покидает ваш сайт. (Apache распознает закрытый сокет и уничтожает экземпляр PHP.) В основном вы можете закрыть сокет со стороны сервера, если вы знаете, что какое-то время не будет никаких данных; последнее сообщение, которое вы отправляете клиенту, - это сказать ему вернуться в определенное время. Например, в случае потоковой передачи акций вы можете закрыть соединение в 20:00 и попросить клиентов вернуться через 8 часов (при условии, что NASDAQ открыт для котировок с 4:00 до 20:00). В пятницу вечером вы говорите им, чтобы они вернулись в понедельник утром. (У меня есть готовящаяся книга по SSE, и я посвящу этому вопросу несколько разделов.)
Что ж, люди утверждают, что 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 ++. Менеджер во мне добавил бы еще одну коробку.
источник
События, отправленные сервером, предназначены для обновления в реальном времени со стороны сервера на сторону клиента. В первом примере соединение с сервером не сохраняется, и клиент пытается подключиться снова каждые 3 секунды, и события, отправленные сервером, не отличаются от опроса ajax.
Итак, чтобы соединение сохранялось, вам нужно обернуть свой код в цикл и постоянно проверять наличие обновлений.
PHP основан на потоках, и большее количество подключенных пользователей заставит сервер исчерпать ресурсы. Это можно решить, контролируя время выполнения скрипта и завершая скрипт, когда оно превышает заданное время (например, 10 минут).
EventSource
API , будет автоматически подключаться снова , так что задержка в приемлемом диапазоне.Кроме того, ознакомьтесь с моей библиотекой PHP для событий , отправляемых сервером , вы можете больше узнать о том, как выполнять события, отправленные сервером в PHP, и упростить код.
источник
Я заметил, что 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, он получит его из таблицы БД.
Я надеюсь, что мне помогут!
источник
Это действительно структурный вопрос о вашем приложении. События в реальном времени - это то, о чем вы хотите думать с самого начала, поэтому вы можете создать свое приложение на основе этого. Если вы написали приложение, которое просто запускает кучу случайных
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 тоже есть свои ограничения, но для соединений через веб-сокеты (и SSE) сейчас это самая мощная технология.
И еще - SSE не так хорош, как кажется. Единственное преимущество веб-сокетов - это то, что пакеты архивируются изначально (ws не архивируются), но с другой стороны, SSE - это одностороннее соединение. Пользователь, если он хочет добавить еще один символ акции в подписку, должен будет сделать запрос ajax (включая все проблемы с контролем происхождения, и запрос будет медленным). В веб-сокетах клиент и сервер обмениваются данными в обоих направлениях в одном открытом соединении, поэтому, если пользователь отправляет торговый сигнал или подписывается на котировку, он просто отправляет строку в уже открытом соединении. И это быстро.
источник