При разработке высоконагруженных систем на базе 1С с использованием HTTP-сервисов программисты часто сталкиваются с задачей сохранения промежуточного состояния пользователя. Возникает естественное желание использовать привычные механизмы, такие как ВременноеХранилище или переменные модуля сеанса, и попытаться «сбросить» накопленные данные в базу в момент закрытия соединения. Однако архитектура HTTP-сервисов в 1С имеет свои критические особенности. Разберем подробно, почему стандартные методы завершения работы не подходят и какие альтернативные решения существуют.
Давайте проанализируем, как работает обычное клиентское приложение и HTTP-сервис. В толстом или тонком клиенте сеанс жестко привязан к жизненному циклу процесса на компьютере пользователя. Как только пользователь закрывает программу, платформа инициирует событие ПередЗавершениемРаботыСистемы. В HTTP-сервисах ситуация иная.
Когда мы работаем с HTTP-сервисами, особенно при включенном параметре «Повторное использование сеансов» (Session Reuse), платформа 1С использует механизм пула сеансов. Это означает, что после того, как ваш метод GET или POST отработал и вернул HTTPСервисОтвет, сеанс не закрывается. Он переходит в состояние «спящего» и возвращается в пул, ожидая следующего запроса. Платформа делает это для экономии ресурсов, чтобы не тратить время на инициализацию параметров сеанса при каждом чихе клиента.
Ключевой момент: завершение сеанса в пуле — это административное событие кластера серверов 1С, происходящее по таймауту (например, через 20 минут бездействия). В этот момент никакой программный код на встроенном языке 1С не исполняется. Платформа просто уничтожает объект сеанса в памяти. Именно поэтому «отловить» этот момент программно невозможно.
Рассмотрим ситуацию, когда программист решает хранить данные во ВременноеХранилище в надежде записать их позже. Выясним, почему это опасно при работе через HTTP:
Поскольку мы не можем полагаться на память сеанса, проанализируем самый надежный подход: хранение промежуточных состояний в базе данных. Для этого идеально подходит Регистр сведений. Вместо того чтобы ждать завершения соединения, мы будем записывать «черновик» данных при каждом запросе.
Предположим, клиент передает нам данные порциями. Создадим регистр КэшДанныхHTTP с измерениями ИдентификаторСессии (GUID) и ресурсом ДанныеJSON (Строка или ХранилищеЗначения).
// Пример сохранения промежуточного состояния
Процедура СохранитьПромежуточноеСостояние(IDСессии, ДанныеДляСохранения)
МенеджерЗаписи = РегистрыСведений.КэшДанныхHTTP.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИдентификаторСессии = IDСессии;
МенеджерЗаписи.ДанныеJSON = ДанныеДляСохранения;
МенеджерЗаписи.ДатаОбновления = ТекущаяДатаСеанса();
МенеджерЗаписи.Записать();
КонецПроцедуры
Этот подход делает ваш сервис stateless (без сохранения состояния на сервере), что является эталоном проектирования HTTP-интерфейсов. Теперь неважно, в какой именно сеанс пула попадет следующий запрос — он всегда сможет считать данные из регистра по переданному идентификатору.
Если вам все же необходимо выполнить какое-то действие именно тогда, когда пользователь «ушел», мы можем реализовать механизм очистки по таймауту. Разберем, как это сделать:
КэшДанныхHTTP.ОчисткаУстаревшихСессийHTTP, которое запускается, например, раз в 5 минут.
// Код регламентного задания
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КэшДанныхHTTP.ИдентификаторСессии,
| КэшДанныхHTTP.ДанныеJSON
|ИЗ
| РегистрСведений.КэшДанныхHTTP КАК КэшДанныхHTTP
|ГДЕ
| КэшДанныхHTTP.ДатаОбновления < &ГраницаВремени";
Запрос.УстановитьПараметр("ГраницаВремени", ТекущаяДатаСеанса() - 1800); // 30 минут
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
Попытка
// Выполняем финальную запись данных
ЗафиксироватьРезультатыРаботы(Результат.ДанныеJSON);
// Удаляем запись из кэша
УдалитьЗаписьКэша(Результат.ИдентификаторСессии);
Исключение
ЗаписьЖурналаРегистрации("HTTP.Очистка", УровеньЖурналаРегистрации.Ошибка, , , ИнформацияОбОшибке().Описание);
КонецПопытки;
КонецЦикла;
Если у вас есть контроль над клиентским приложением (например, это мобильное приложение или сторонний веб-сервис), спроектируйте API так, чтобы оно содержало метод POST /logout или POST /complete. Проанализируем преимущества этого способа: клиент сам сообщает серверу, что работа завершена. В этом обработчике вы можете выполнить всю необходимую логику по переносу данных из временных таблиц в основные документы или справочники.
Важно помнить, что клиент может просто закрыть вкладку браузера или потерять связь. Поэтому Решение №3 всегда должно дополняться Решением №2 (регламентным заданием), которое подстрахует систему в случае внезапного обрыва соединения.
Если вам нужно управлять тем, как долго сеансы «живут» в пуле, следует обратить внимание на файл публикации default.vrd. Рассмотрим важные параметры:
Увеличение sessionMaxAge позволит клиенту чаще «попадать» в тот же сеанс (при низкой нагрузке), но это не решает фундаментальную проблему отсутствия обработчика закрытия. Это лишь отсрочит момент уничтожения данных во временном хранилище.
Подведем итог нашему разбору. Попытка применить логику настольных приложений (событие закрытия системы) к HTTP-сервисам в 1С — это тупиковый путь. Проанализировав работу платформы, мы пришли к следующим выводам:
Соблюдение этих архитектурных правил позволит вам создавать отказоустойчивые и производительные интеграционные решения на платформе 1С, не зависящие от специфики работы пула сеансов.