В разработке крупных корпоративных систем часто возникает задача мгновенной передачи информации с сервера на сторону клиента (так называемая push-технология). Примерами таких задач могут служить интеграция с SIP-телефонией (когда карточка входящего звонка должна открыться у оператора в ту же секунду), оперативное оповещение о согласовании документов или отображение прогресса длительных фоновых операций на сервере.
Классическая архитектура 1С построена по принципу pull-модели, в которой инициатором любого обмена данными всегда выступает клиент. Сервер "сам по себе" не имеет возможности напрямую инициировать вызов клиентской процедуры в произвольный момент времени. В данной статье мы подробно разберем, почему стандартные подходы часто оказываются неэффективными, проанализируем ограничения платформы и рассмотрим как альтернативные обходные пути, так и новейшее штатное решение, появившееся в платформе 1С:Предприятие 8.3.26.
Большинство разработчиков для решения задачи оповещения используют метод ПодключитьОбработчикОжидания на клиенте. С заданной периодичностью (например, каждые 3–5 секунд) клиент обращается к серверу, опрашивая определенный регистр сведений или таблицу задач на предмет появления новых записей. Рассмотрим подробнее, к каким проблемам это приводит при росте информационной базы:
В процессе обсуждения на форумах часто всплывают ошибочные мнения о существовании простых "нативных" альтернатив. Проанализируем самые популярные заблуждения:
ОповеститьОбИзменении. Этот метод предназначен исключительно для локального оповещения форм в рамках одного и того же клиентского сеанса. Он сообщает открытым динамическим спискам текущего сеанса, что данные определенного типа были изменены, чтобы они обновили кэш. Передать сигнал в сеанс другого пользователя или получить его с сервера (например, из регламентного задания) этот метод физически не может.НапоминанияПользователя действительно происходят на сервере. Однако то, как клиент "узнает" об их появлении, под капотом БСП реализовано все тем же периодическим опросом через обработчик ожидания. Чуда здесь нет — это лишь удобная обертка над таймером.MSG) привязаны к конкретной операционной системе, требуют сложной настройки прав доступа в сети и выводят сообщения на уровне ОС, а не внутри интерфейса 1С.Ниже мы подробно разберем три архитектурных решения проблемы: революционное нативное решение в версии 8.3.26 и два проверенных временем обходных пути для более ранних версий платформы.
---
Начиная с версии платформы 1С:Предприятие 8.3.26, разработчики добавили встроенный и абсолютно бесплатный инструмент — Менеджер уведомлений клиента. Этот механизм работает напрямую через сетевой протокол без использования Системы взаимодействия, не требует покупки КОРП-лицензий и доступен на стандартных ПРОФ-тарифах. Он позволяет серверным процедурам отправлять точечные push-уведомления в конкретные сеансы пользователей.
Рассмотрим, как реализовать этот механизм по шагам.
При запуске клиентского приложения (например, в модуле управляемого приложения или при открытии ключевой формы) нам необходимо зарегистрировать обработчик, который будет "слушать" серверные события. Для этого используется свойство глобального контекста УведомленияКлиента.
// В модуле управляемого приложения или при создании формы на клиенте
Процедура ПриНачалеРаботыСистемы()
// Подключаем обработчик для определенного ключа события
УведомленияКлиента.ПодключитьОбработчик("ВажноеСерверноеСобытие",
Новый ОписаниеОповещения("ОбработатьСерверноеУведомление", ЭтотОбъект));
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьСерверноеУведомление(ДанныеУведомления, ДополнительныеПараметры) Экспорт
// ДанныеУведомления — это структура или объект произвольного типа, переданный с сервера
ПоказатьОповещениеПользователя("Внимание!", , ДанныеУведомления.ТекстСообщения);
// Здесь можно выполнить любые клиентские действия, например, открыть форму документа
Если ДанныеУведомления.Свойство("СсылкаНаДокумент") Тогда
ПоказатьЗначение(, ДанныеУведомления.СсылкаНаДокумент);
КонецЕсли;
КонецПроцедуры
На сервере (в обработчике проведения документа, модуле бизнес-процесса или регламентном задании) мы определяем список адресатов и отправляем им данные. Платформа идентифицирует получателей по номерам их сеансов.
&НаСервере
Процедура ОтправитьУведомлениеАдресатам(ТекстСообщения, СсылкаНаОбъект, ИмяПолучателя)
// Формируем список номеров сеансов целевого пользователя
МассивНомеровСеансов = Новый Массив;
АктивныеСеансы = ПолучитьСеансыИнформационнойБазы();
Для Каждого ТекущийСеанс Из АктивныеСеансы Цикл
Если ТекущийСеанс.ИмяПользователя = ИмяПолучателя Тогда
МассивНомеровСеансов.Добавить(ТекущийСеанс.НомерСеанса);
КонецЕсли;
КонецЦикла;
// Если пользователь сейчас в системе — отправляем push-уведомление
Если МассивНомеровСеансов.Количество() > 0 Тогда
ДанныеПакета = Новый Структура;
ДанныеПакета.Вставить("ТекстСообщения", ТекстСообщения);
ДанныеПакета.Вставить("СсылкаНаДокумент", СсылкаНаОбъект);
// Отправляем уведомление через менеджер уведомлений
УведомленияКлиента.ОтправитьУведомление("ВажноеСерверноеСобытие", ДанныеПакета, МассивНомеровСеансов);
КонецЕсли;
КонецПроцедуры
В версии 8.3.26 у объекта ФоновоеЗадание появилось новое системное свойство НомерРодительскогоСеанса. Это позволяет длительным фоновым расчетам точечно слать уведомления о проценте выполнения задачи прямо в тот клиентский сеанс, который запустил эту операцию, гарантируя мгновенную обратную связь без перегрузки базы данных запросами. Для организации контроля длительных операций на предыдущих версиях платформы можно использовать пример отслеживания прогресса фоновых заданий без БСП.
---
Если ваша информационная база работает на версии платформы ниже 8.3.26, а использовать Систему взаимодействия нет возможности, отличной альтернативой стандартному опросу станет концепция Long Polling (длинные запросы).
При обычном опросе клиент шлет частые запросы и сразу получает ответ (пустой или с данными). При Long Polling клиент отправляет запрос, а сервер намеренно удерживает соединение открытым до тех пор, пока на сервере не произойдет нужное событие (или не истечет таймаут, например, 30 секунд). Только тогда сервер возвращает ответ, а клиент, обработав его, сразу же открывает новое "длинное" соединение.
Рассмотрим схему реализации этого подхода через публикацию собственного HTTP-сервиса 1С.
Создадим HTTP-сервис PushService с шаблоном пути /wait/{ИмяПользователя}.
Функция waitGet(Запрос)
ИмяПользователя = Запрос.ПараметрыШаблона.Получить("ИмяПользователя");
ВремяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах();
ТаймаутОжидания = 25000; // Удерживаем соединение максимум 25 секунд
ЕстьДанные = Ложь;
Результат = "";
Пока (ТекущаяУниверсальнаяДатаВМиллисекундах() - ВремяНачала) < ТаймаутОжидания Цикл
// Проверяем наличие событий в быстром источнике (например, во временной таблице в ОЗУ или РС)
События = ПроверитьНовыеСобытияДляПользователя(ИмяПользователя);
Если События.Количество() > 0 Тогда
ЕстьДанные = Истина;
Результат = СформироватьJSONДанных(События);
Прервать;
КонецЕсли;
// Приостанавливаем поток на сервере на 1 секунду, чтобы не грузить процессор циклом
ТекущийСистемныйИнструмент.Пауза(1);
КонецЦикла;
Ответ = Новый HTTPСервисОтвет(?(ЕстьДанные, 200, 204)); // 204 No Content, если ничего не произошло
Если ЕстьДанные Тогда
Ответ.УстановитьТелоИзСтроки(Результат);
КонецЕсли;
Возврат Ответ;
КонецФункции
На стороне клиента мы запускаем асинхронный процесс, который вызывает данный HTTP-сервис в фоновом режиме. Как только приходит ответ (код 200), мы обрабатываем его и сразу же рекурсивно инициируем следующий запрос.
Преимущество метода: Количество запросов снижается в сотни раз по сравнению с классическим опросом, а реакция на событие происходит в ту же секунду, когда сервер записал событие и прервал цикл ожидания.
---
Еще один продвинутый способ реализации мгновенных уведомлений на старых платформах — использование встроенного в тонкий клиент движка WebKit через Поле HTML-документа. Сама платформа 1С до версии 8.3.26 не умеет работать со стандартными веб-сокетами напрямую, но это отлично делает JavaScript внутри HTML-поля. Для прямого взаимодействия с сокетами без браузерного поля можно использовать внешнюю компоненту WebSocket-клиента.
Схема взаимодействия выглядит следующим образом:
Рассмотрим пример реализации JavaScript-кода, загружаемого в Поле HTML-документа:
<html>
<head>
<script type="text/javascript">
var socket;
function connect() {
// Подключаемся к локальному или внешнему веб-сокет серверу
socket = new WebSocket("ws://broker.local:8080/notifications");
socket.onmessage = function(event) {
var messageData = JSON.parse(event.data);
// Передаем данные обратно в платформу 1С
// Используем штатный механизм интеграции JS и 1С
if (typeof window.1cConnector !== 'undefined') {
window.1cConnector.postMessage(JSON.stringify(messageData));
}
};
socket.onclose = function(e) {
// В случае обрыва связи пробуем переподключиться через 5 секунд
setTimeout(function() { connect(); }, 5000);
};
}
window.onload = connect;
</script>
</head>
<body style="margin:0; padding:0; overflow:hidden;"></body>
</html>
В самой форме 1С достаточно обрабатывать событие поля HTML-документа ПриНажатии или событие получения сообщения от JS-коннектора (в зависимости от используемой версии платформы). Этот метод обеспечивает фантастическую скорость доставки (менее 50 миллисекунд) и не нагружает сервер 1С фоновыми процессами ожидания.
---
Давайте сопоставим все три разобранных варианта, чтобы вы могли выбрать наиболее подходящий архитектурный путь для своей системы:
Подведем итог: если у вас есть техническая возможность обновить платформу до актуального релиза 8.3.26 — обязательно сделайте это. Новый объект МенеджерУведомленийКлиента полностью закрывает давний пробел в архитектуре 1С, позволяя раз и навсегда отказаться от "костылей" с периодическими обработчиками ожидания. В случае работы на более старых версиях платформы отдавайте предпочтение связке Long Polling или интеграции через Поле HTML-документа, сохраняя производительность СУБД на высоком уровне.