Как организовать ограничение скорости отправки писем и задержку на стороне сервера в 1С

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

При массовом согласовании документов или групповой обработке данных часто возникает задача отправки большого количества почтовых уведомлений. Если отправлять письма скопом (например, пачкой из 50 и более штук), внешние почтовые серверы и спам-фильтры могут расценить это как спам-атаку и заблокировать учетную запись либо временно отклонить прием сообщений (ошибки SMTP с кодами 4xx/5xx). Разберем, как грамотно организовать троттлинг (ограничение частоты отправки, например, не более 20 писем в минуту) на стороне сервера 1С.

Архитектурный подход: паттерн Outbox через регистр сведений

Попытка отправлять письма прямо в момент проведения документов или создания фонового задания «на лету» с искусственными паузами — не самое надежное решение. Наиболее устойчивым подходом является организация асинхронной очереди исходящих сообщений через регистр сведений.

Рассмотрим структуру и логику работы такого механизма:

  1. Фиксация события: При согласовании заявки в коде не вызывается непосредственная отправка по SMTP. Вместо этого в специализированный регистр сведений (например, ОчередьИсходящихСообщений) записываются данные письма: получатель, тема, тело, вложения, дата постановки в очередь и начальный статус КОтправке.
  2. Регламентная обработка: Отдельное регламентное задание запускается по расписанию (например, 1 раз в минуту), выбирает разрешенную порцию писем (первые 20 записей) и выполняет отправку.
  3. Обновление статусов: После успешной отправки статус записи меняется на Отправлено (либо запись удаляется). В случае ошибки увеличивается счетчик попыток и фиксируется текст ошибки.

Реализация порционной выборки и отправки

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


Процедура ОтправитьОчередьПисем() Экспорт
    
    // 1. Выбираем порцию не более 20 неотправленных писем
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 20
        |	Очередь.ИдентификаторСообщения КАК Идентификатор,
        |	Очередь.АдресПолучателя КАК АдресПолучателя,
        |	Очередь.Тема КАК Тема,
        |	Очередь.ТелоПисьма КАК ТелоПисьма
        |ИЗ
        |	РегистрСведений.ОчередьИсходящихСообщений КАК Очередь
        |ГДЕ
        |	Очередь.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыОтправки.КОтправке)
        |УПОРЯДОЧИТЬ ПО
        |	Очередь.ДатаСоздания ВОЗР";
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Не Выборка.Следующий() Тогда
        Возврат;
    КонецЕсли;
    
    // 2. Инициализируем профиль и объект почты
    Профиль = Новый ИнтернетПочтовыйПрофиль;
    // ... Заполнение параметров SMTP сервера, логина и пароля ...
    
    Почта = Новый ИнтернетПочта;
    
    Попытка
        Почта.Подключиться(Профиль, ПротоколИнтернетПочты.SMTP);
    Исключение
        ЗаписьЖурналаРегистрации("ОтправкаПочты", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
        Возврат;
    КонецПопытки;
    
    // 3. Отправляем выбранную пачку в рамках одной SMTP-сессии
    Выборка.Сбросить();
    Пока Выборка.Следующий() Цикл
        
        Письмо = Новый ИнтернетПочтовоеСообщение;
        Письмо.Получатели.Добавить(Выборка.АдресПолучателя);
        Письмо.Тема = Выборка.Тема;
        Письмо.Тексты.Добавить(Выборка.ТелоПисьма);
        
        Попытка
            Почта.Послать(Письмо);
            
            // Фиксируем успешную отправку
            ЗафиксироватьСтатусПисьма(Выборка.Идентификатор, Перечисления.СтатусыОтправки.Отправлено);
        Исключение
            ЗафиксироватьСтатусПисьма(Выборка.Идентификатор, Перечисления.СтатусыОтправки.Ошибка, ОписаниеОшибки());
        КонецПопытки;
        
    КонецЦикла;
    
    Почта.Отключиться();
    
КонецПроцедуры

Оптимизация SMTP-сессий

Проанализируем важную деталь при отправке пачек писем: вызов методов Подключиться() и Отключиться() не должен происходить внутри цикла для каждого отдельного письма. Постоянное переподключение нагружает почтовый сервер и само по себе может спровоцировать срабатывание защиты от подбора паролей. Оптимально открыть соединение один раз перед циклом, отправить все 20 писем метода Послать() и затем разорвать соединение.

Организация задержки на уровне платформы

Если логика бизнес-процесса все же требует отправки в рамках одного фонового задания с контролируемым интервалом между сообщениями, необходимо учитывать версию платформы 1С:

  1. Начиная с версии 1С:Предприятие 8.3.25: Появился штатный серверный метод глобального контекста ВызватьПаузу(<Интервал>), где интервал задается в миллисекундах. Этот метод усыпляет поток на стороне сервера и не расходует процессорные ресурсы. Например, вызов ВызватьПаузу(3000) сделает паузу в 3 секунды между отправками писем.
  2. Для более ранних версий платформы: Штатного метода серверной паузы нет. Категорически не рекомендуется использовать пустые циклы ожидания по времени вида Пока ТекущаяДата() < ..., так как они загружают ядро процессора сервера на 100%. В таких конфигурациях разделение отправки через регистр сведений и регламентное задание раз в минуту является единственно правильным решением.

Альтернатива: Локальный почтовый релей (MTA)

Если рассылок очень много и не хочется усложнять архитектуру 1С очередями и контролем лимитов, можно применить инфраструктурное решение — развернуть локальный почтовый релей (например, Postfix, Exim или hMailServer):

  1. 1С моментально отправляет все 50 или 100 писем на локальный SMTP-сервер внутри сети без задержек.
  2. Локальный релей складывает их в собственную очередь и осуществляет отправку на внешний почтовый сервер со строго заданным лимитом скорости (rate limiting), выполняя повторные попытки при сбоях связи.
← На главную