[ИНТЕРЕСНО] Server-Sent Events - Уведомления с сервера в браузер в реальном времени
Всем привет! Искал простой способ отправлять уведомления о смене статуса заказа в браузер пользователя. Из вариантов были websocket и сторонние сервисы push-уведомлений. С websocket я разобраться не смог, пробовал запустить workerman на shared-хостинге, но не вышло. Сторонние сервисы вроде sendpulse или comet-сервера не устраивают, потому что они сторонние, их оставлю на крайний случай.
И совершенно случайно я наткнулся на Server-Sent Events. Всё с ними хорошо, кроме одного, опытным путём я понял, что он должен быть запущен всегда, т.е. нельзя запустить его из другого скрипта и передать параметры. Или можно, если кто-то знает как, напишите в комментариях.
Так или иначе я его для своих задач приспособил. Вопрос такой: насколько это будет нагружать сервер и, если никто не знает, то подскажите как можно провести тест под нагрузкой? Предполагается от 1 до 5 тысяч клиентов одновременно.
Собственно кода немного. Плагин на событие msOnCreateOrder, который записывает в cookie номер заказа, который в дальнейшем используется как имя события, на которое подписан клиент.
Ещё один плагин на событие msOnChangeOrderStatus записывает статус заказа в файл, который потом мы проверять будем. А в файл пишу, потому что не придумал как иначе передать данные. Если делать запросы в БД из основного скрипта, так он срабатывает каждые 10 секунд, при 1000 клиентов боюсь что-нибудь ляжет, но это не точно и проверять не хочется.
Основной скрипт выглядит так
На тот случай если кто-то захочет протестировать у себя, приведу ещё код шаблона, там самое главное это js.
И совершенно случайно я наткнулся на Server-Sent Events. Всё с ними хорошо, кроме одного, опытным путём я понял, что он должен быть запущен всегда, т.е. нельзя запустить его из другого скрипта и передать параметры. Или можно, если кто-то знает как, напишите в комментариях.
Так или иначе я его для своих задач приспособил. Вопрос такой: насколько это будет нагружать сервер и, если никто не знает, то подскажите как можно провести тест под нагрузкой? Предполагается от 1 до 5 тысяч клиентов одновременно.
Собственно кода немного. Плагин на событие msOnCreateOrder, который записывает в cookie номер заказа, который в дальнейшем используется как имя события, на которое подписан клиент.
<?php
$num = str_replace('/', '-', $msOrder->get('num')); // со слэшем в куки нельзя
setcookie('order_num', $num, time()+3600*6, '/', $modx->getOption('http_host'));
Ещё один плагин на событие msOnChangeOrderStatus записывает статус заказа в файл, который потом мы проверять будем. А в файл пишу, потому что не придумал как иначе передать данные. Если делать запросы в БД из основного скрипта, так он срабатывает каждые 10 секунд, при 1000 клиентов боюсь что-нибудь ляжет, но это не точно и проверять не хочется.
<?php
$output = array();
$fileData = array();
$statusObj = $order->getOne('Status');
$path = $_SERVER['DOCUMENT_ROOT'].'/assets/notifydata.json';
$num = str_replace('/', '-', $order->get('num'));
$output[$num]['status'] = $statusObj->get('name');
if(file_exists($path)){
$fileData = json_decode(file_get_contents($path), 1);
if($fileData){
$output = array_merge($fileData,$output);
}
}
$data = json_encode($output,JSON_UNESCAPED_UNICODE);
$result = file_put_contents($path, $data, LOCK_EX);
Основной скрипт выглядит так
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
echo "retry: 10000" . PHP_EOL;
// Функция отправки сообщения
function sendMsg()
{
$path = $_SERVER['DOCUMENT_ROOT'].'/assets/notifydata.json';
$event = trim($_COOKIE['order_num']);
$id = time();
if(file_exists($path)){
$fileData = json_decode(file_get_contents($path), 1);
$msg = $fileData[$event]['status'];
}
if($msg){
echo "event:" . $event . PHP_EOL;
echo "id: $id" . PHP_EOL;
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
die();
}
}
while(true) {
// Отправляем полученное время в сообщении
sendMsg();
}
Взял его отсюда и немного подправил под свои нужды.На тот случай если кто-то захочет протестировать у себя, приведу ещё код шаблона, там самое главное это js.
<div id="messageLog">[Лог сообщений:]</div>
<div id="timeDisplay">[Время не получено]</div>
<div id="controls">
<button onclick="startListening()">Начать прослушивание</button>
<button onclick="stopListening()">Остановить прослушивание</button>
</div>
<style>
body {
font-family: Verdana;
font-size: 12px;
}
#messageLog {
width: 900px;
height: 230px;
border: darkgray 2px solid;
border-radius: 5px;
margin: 20px;
padding: 10px;
overflow: scroll;
overflow-x: hidden;
}
#timeDisplay {
color: darkblue;
font-size: 30px;
width: 400px;
font-weight: bold;
border: black 1px solid;
border-radius: 10px;
margin: 10px 20px 10px 20px;
padding: 10px;
background-color: #FBF3CB;
}
button {
padding: 8px;
margin: 5px 0px 0px 20px;
}
</style>
<script>
startListening();
document.cookie = "order_status=0; max-age=0";
function getCookies(){
let rawCookies = document.cookie.split(';'),
cookie = { };
rawCookies.forEach(function(el){
el = el.split('=');
cookie[el[0].trim()] = el[1].trim();
});
return cookie;
}
function startListening() {
let cookie = document.cookie.split(';'),
cookies = getCookies(),
orderNum = cookies.order_num;
source = new EventSource("assets/server_events.php");
source.addEventListener(orderNum, receiveMessage);
messageLog.innerHTML += "
" + "Начинаем слушать сообщения."
}
function receiveMessage(event) {
let cookies = getCookies(),
orderStatus = cookies.order_status;
if(event.data.indexOf(orderStatus) == -1 || !orderStatus){
messageLog.innerHTML += "
" + event.data;
timeDisplay.innerHTML = event.data;
document.cookie = "order_status="+event.data+"; max-age=21600";
}
}
function stopListening() {
try{
source.close();
messageLog.innerHTML += "
" + "Больше не прослушивать сообщения."
}catch{}
}
</script>
Поблагодарить автора
Отправить деньги
Комментарии: 2
Я правильно понял, что это будет работать, только если клиент находится на сайте?
Да, правильно, но в случае если клиент сайт покинул, уведомления ему не нужны.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.