Перейти к содержимому

Сообщения на страницу без перезагрузки - отправка, подписка, каналы

Отправляем события на открытую страницу: обновляем остаток товара, счётчик заказов или сообщение менеджеру без перезагрузки и без опроса сервера.

Решение

Отправляем событие пользователю:

\Bitrix\Main\Loader::includeModule('pull');
\Bitrix\Pull\Event::add([$userId], [
'module_id' => 'vendor.shop',
'command' => 'order_status',
'params' => ['orderId' => 1234, 'status' => 'Отгружен'],
]);
// событие уходит только тем, у кого страница открыта прямо сейчас

Событие адресуется конкретным пользователям и уходит только на их открытые страницы. Это не очередь: сообщение не сохраняется и не догоняет того, кто зашёл через минуту.

Подписываемся в браузере:

BX.ready(() => {
BX.PULL.subscribe({
moduleId: 'vendor.shop',
command: 'order_status',
callback: (data) => updateOrderRow(data.orderId, data.status),
});
});

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

Открываем общий канал для гостей:

CPullWatch::Add($userId, 'STOCK_' . $productId); // подписка на тег
CPullWatch::AddToStack('STOCK_' . $productId, [
'module_id' => 'vendor.shop',
'command' => 'stock_changed',
'params' => ['quantity' => 3],
]);

Личный канал есть только у вошедшего на сайт пользователя. Обновление остатка на витрине интересно всем сразу, поэтому его шлют в общий канал с тегом, а не каждому по отдельности.

Разбираем недоставку:

Окно терминала
curl -sI https://example.com/bitrix/sub/ | head -3
tail -20 /var/log/push-server-*.log
# сначала соединение с сервером очередей, потом уже событие
systemctl status push-server-sub push-server-pub --no-pager | head -6
# обе части сервера очередей должны быть запущены

Разбор недоставки идёт от соединения со страницей к самому событию. Если страница не подключена к серверу очередей, обсуждать команды и подписки рано: не дойдёт ничего вне зависимости от кода.

Сами сообщения стоит делать мелкими и без всякого важного содержимого. Событие говорит «данные изменились», а страница запрашивает подробности обычным запросом - тогда потеря одного сообщения не ломает картину.

Число открытых страниц с подпиской стоит держать в голове при расчёте нагрузки. Каждая такая страница - это живое соединение, и тысяча посетителей на витрине означает тысячу соединений к серверу очередей.

Живые обновления не заменяют собой проверку на сервере. Кнопка «купить», разблокированная сообщением об остатке, всё равно обязана проверить наличие при добавлении в корзину.

Типичные проблемы

Событие не доходит до гостя.

Оно отправлено в личный канал, а у гостя такого канала попросту нет. Гостям сообщения шлют через общий канал с тегом подписки.

Подписка не срабатывает.

Код подписки выполнен до загрузки библиотеки сообщений. Подписку ставят только после её полной готовности.

Сообщение не пришло тому, кто открыл страницу позже.

События нигде не хранятся и не догоняют опоздавших. Страница при открытии запрашивает состояние обычным запросом.

Ничего не приходит вообще.

Страница вообще не соединена с сервером очередей. Разбор начинают именно с этого соединения, а не с команд и подписок.

Данные в сообщении устарели.

В событие положено само значение, а не признак изменения. Подробности запрашивают отдельным запросом уже после сигнала.

Частые вопросы

Нужен ли отдельный сервер для этого?

Да, сервер очередей ставится вместе с окружением. Без него сообщения никуда не уходят.

Сколько сообщений выдержит канал?

Много, но каждое стоит соединения и памяти. Обновление остатка на каждый чих превращается в собственную нагрузку.

Можно ли слать сообщение всем посетителям?

Да, общим каналом, но осторожно: событие уйдёт всем открытым страницам сразу. Для редких важных уведомлений это уместно.

Как проверить доставку при отладке?

Служебной командой в консоли браузера и журналом сервера очередей. Проверять по поведению интерфейса дольше и ненадёжнее.

Смежное

Первоисточник