Как отловить завершение HTTP-сессии или соединения в 1С:Предприятие

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке высоконагруженных систем на базе 1С с использованием HTTP-сервисов программисты часто сталкиваются с задачей сохранения промежуточного состояния пользователя. Возникает естественное желание использовать привычные механизмы, такие как ВременноеХранилище или переменные модуля сеанса, и попытаться «сбросить» накопленные данные в базу в момент закрытия соединения. Однако архитектура HTTP-сервисов в 1С имеет свои критические особенности. Разберем подробно, почему стандартные методы завершения работы не подходят и какие альтернативные решения существуют.

Анализ проблемы: почему нет события «ПередЗавершениемРаботыСистемы»?

Давайте проанализируем, как работает обычное клиентское приложение и HTTP-сервис. В толстом или тонком клиенте сеанс жестко привязан к жизненному циклу процесса на компьютере пользователя. Как только пользователь закрывает программу, платформа инициирует событие ПередЗавершениемРаботыСистемы. В HTTP-сервисах ситуация иная.

Когда мы работаем с HTTP-сервисами, особенно при включенном параметре «Повторное использование сеансов» (Session Reuse), платформа 1С использует механизм пула сеансов. Это означает, что после того, как ваш метод GET или POST отработал и вернул HTTPСервисОтвет, сеанс не закрывается. Он переходит в состояние «спящего» и возвращается в пул, ожидая следующего запроса. Платформа делает это для экономии ресурсов, чтобы не тратить время на инициализацию параметров сеанса при каждом чихе клиента.

Ключевой момент: завершение сеанса в пуле — это административное событие кластера серверов 1С, происходящее по таймауту (например, через 20 минут бездействия). В этот момент никакой программный код на встроенном языке 1С не исполняется. Платформа просто уничтожает объект сеанса в памяти. Именно поэтому «отловить» этот момент программно невозможно.

Проблема использования Временного хранилища

Рассмотрим ситуацию, когда программист решает хранить данные во ВременноеХранилище в надежде записать их позже. Выясним, почему это опасно при работе через HTTP:

  1. Отсутствие гарантии аффинити (привязки): Если от одного клиента придет два запроса почти одновременно, они могут попасть в разные сеансы из пула. Данные, помещенные в хранилище в первом запросе, будут просто недоступны во втором.
  2. Непредсказуемость очистки: Данные во временном хранилище живут столько, сколько живет сеанс. Если сеанс «завис» в пуле на долгое время, память будет расходоваться неэффективно. А если сеанс будет принудительно завершен администратором или кластером, данные исчезнут бесследно.

Решение №1: Архитектура на базе регистров сведений (State Table)

Поскольку мы не можем полагаться на память сеанса, проанализируем самый надежный подход: хранение промежуточных состояний в базе данных. Для этого идеально подходит Регистр сведений. Вместо того чтобы ждать завершения соединения, мы будем записывать «черновик» данных при каждом запросе.

Предположим, клиент передает нам данные порциями. Создадим регистр КэшДанныхHTTP с измерениями ИдентификаторСессии (GUID) и ресурсом ДанныеJSON (Строка или ХранилищеЗначения).


// Пример сохранения промежуточного состояния
Процедура СохранитьПромежуточноеСостояние(IDСессии, ДанныеДляСохранения)
    МенеджерЗаписи = РегистрыСведений.КэшДанныхHTTP.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.ИдентификаторСессии = IDСессии;
    МенеджерЗаписи.ДанныеJSON = ДанныеДляСохранения;
    МенеджерЗаписи.ДатаОбновления = ТекущаяДатаСеанса();
    МенеджерЗаписи.Записать();
КонецПроцедуры

Этот подход делает ваш сервис stateless (без сохранения состояния на сервере), что является эталоном проектирования HTTP-интерфейсов. Теперь неважно, в какой именно сеанс пула попадет следующий запрос — он всегда сможет считать данные из регистра по переданному идентификатору.

Решение №2: Имитация события завершения через Регламентные задания

Если вам все же необходимо выполнить какое-то действие именно тогда, когда пользователь «ушел», мы можем реализовать механизм очистки по таймауту. Разберем, как это сделать:

  1. При каждом запросе к HTTP-сервису мы обновляем дату активности в нашем регистре КэшДанныхHTTP.
  2. Создаем регламентное задание ОчисткаУстаревшихСессийHTTP, которое запускается, например, раз в 5 минут.
  3. Регламентное задание ищет записи, которые не обновлялись более заданного времени (например, 30 минут).
  4. Для найденных «брошенных» сессий выполняется логика завершения (финальная запись в основные таблицы, логирование и т.д.), после чего временные записи удаляются.

// Код регламентного задания
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	КэшДанныхHTTP.ИдентификаторСессии,
    |	КэшДанныхHTTP.ДанныеJSON
    |ИЗ
    |	РегистрСведений.КэшДанныхHTTP КАК КэшДанныхHTTP
    |ГДЕ
    |	КэшДанныхHTTP.ДатаОбновления < &ГраницаВремени";

Запрос.УстановитьПараметр("ГраницаВремени", ТекущаяДатаСеанса() - 1800); // 30 минут
Результат = Запрос.Выполнить().Выбрать();

Пока Результат.Следующий() Цикл
    Попытка
        // Выполняем финальную запись данных
        ЗафиксироватьРезультатыРаботы(Результат.ДанныеJSON);
        // Удаляем запись из кэша
        УдалитьЗаписьКэша(Результат.ИдентификаторСессии);
    Исключение
        ЗаписьЖурналаРегистрации("HTTP.Очистка", УровеньЖурналаРегистрации.Ошибка, , , ИнформацияОбОшибке().Описание);
    КонецПопытки;
КонецЦикла;

Решение №3: Явный метод Logout

Если у вас есть контроль над клиентским приложением (например, это мобильное приложение или сторонний веб-сервис), спроектируйте API так, чтобы оно содержало метод POST /logout или POST /complete. Проанализируем преимущества этого способа: клиент сам сообщает серверу, что работа завершена. В этом обработчике вы можете выполнить всю необходимую логику по переносу данных из временных таблиц в основные документы или справочники.

Важно помнить, что клиент может просто закрыть вкладку браузера или потерять связь. Поэтому Решение №3 всегда должно дополняться Решением №2 (регламентным заданием), которое подстрахует систему в случае внезапного обрыва соединения.

Настройка времени жизни сеансов в default.vrd

Если вам нужно управлять тем, как долго сеансы «живут» в пуле, следует обратить внимание на файл публикации default.vrd. Рассмотрим важные параметры:

Увеличение sessionMaxAge позволит клиенту чаще «попадать» в тот же сеанс (при низкой нагрузке), но это не решает фундаментальную проблему отсутствия обработчика закрытия. Это лишь отсрочит момент уничтожения данных во временном хранилище.

Итоги и рекомендации

Подведем итог нашему разбору. Попытка применить логику настольных приложений (событие закрытия системы) к HTTP-сервисам в 1С — это тупиковый путь. Проанализировав работу платформы, мы пришли к следующим выводам:

  1. Забудьте про Временное хранилище для хранения состояния между разными HTTP-запросами. Это ненадежно из-за пула сеансов.
  2. Используйте базу данных (Регистры сведений) как промежуточное хранилище «черновиков».
  3. Реализуйте фоновую очистку через регламентные задания для обработки ситуаций, когда клиент не завершил работу явно.
  4. Проектируйте stateless-интерфейсы. Каждый запрос должен содержать достаточно информации (токен, ID сессии) для идентификации контекста в базе данных.

Соблюдение этих архитектурных правил позволит вам создавать отказоустойчивые и производительные интеграционные решения на платформе 1С, не зависящие от специфики работы пула сеансов.

← На главную