При массовом согласовании документов или групповой обработке данных часто возникает задача отправки большого количества почтовых уведомлений. Если отправлять письма скопом (например, пачкой из 50 и более штук), внешние почтовые серверы и спам-фильтры могут расценить это как спам-атаку и заблокировать учетную запись либо временно отклонить прием сообщений (ошибки SMTP с кодами 4xx/5xx). Разберем, как грамотно организовать троттлинг (ограничение частоты отправки, например, не более 20 писем в минуту) на стороне сервера 1С.
Попытка отправлять письма прямо в момент проведения документов или создания фонового задания «на лету» с искусственными паузами — не самое надежное решение. Наиболее устойчивым подходом является организация асинхронной очереди исходящих сообщений через регистр сведений.
Рассмотрим структуру и логику работы такого механизма:
ОчередьИсходящихСообщений) записываются данные письма: получатель, тема, тело, вложения, дата постановки в очередь и начальный статус КОтправке.Отправлено (либо запись удаляется). В случае ошибки увеличивается счетчик попыток и фиксируется текст ошибки.Разберем пример кода процедуры регламентного задания. Обратите внимание, что для предотвращения повторной отправки одних и тех же писем несколькими потоками следует использовать управляемые блокировки данных.
Процедура ОтправитьОчередьПисем() Экспорт
// 1. Выбираем порцию не более 20 неотправленных писем
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 20
| Очередь.ИдентификаторСообщения КАК Идентификатор,
| Очередь.АдресПолучателя КАК АдресПолучателя,
| Очередь.Тема КАК Тема,
| Очередь.ТелоПисьма КАК ТелоПисьма
|ИЗ
| РегистрСведений.ОчередьИсходящихСообщений КАК Очередь
|ГДЕ
| Очередь.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыОтправки.КОтправке)
|УПОРЯДОЧИТЬ ПО
| Очередь.ДатаСоздания ВОЗР";
Выборка = Запрос.Выполнить().Выбрать();
Если Не Выборка.Следующий() Тогда
Возврат;
КонецЕсли;
// 2. Инициализируем профиль и объект почты
Профиль = Новый ИнтернетПочтовыйПрофиль;
// ... Заполнение параметров SMTP сервера, логина и пароля ...
Почта = Новый ИнтернетПочта;
Попытка
Почта.Подключиться(Профиль, ПротоколИнтернетПочты.SMTP);
Исключение
ЗаписьЖурналаРегистрации("ОтправкаПочты", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
Возврат;
КонецПопытки;
// 3. Отправляем выбранную пачку в рамках одной SMTP-сессии
Выборка.Сбросить();
Пока Выборка.Следующий() Цикл
Письмо = Новый ИнтернетПочтовоеСообщение;
Письмо.Получатели.Добавить(Выборка.АдресПолучателя);
Письмо.Тема = Выборка.Тема;
Письмо.Тексты.Добавить(Выборка.ТелоПисьма);
Попытка
Почта.Послать(Письмо);
// Фиксируем успешную отправку
ЗафиксироватьСтатусПисьма(Выборка.Идентификатор, Перечисления.СтатусыОтправки.Отправлено);
Исключение
ЗафиксироватьСтатусПисьма(Выборка.Идентификатор, Перечисления.СтатусыОтправки.Ошибка, ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Почта.Отключиться();
КонецПроцедуры
Проанализируем важную деталь при отправке пачек писем: вызов методов Подключиться() и Отключиться() не должен происходить внутри цикла для каждого отдельного письма. Постоянное переподключение нагружает почтовый сервер и само по себе может спровоцировать срабатывание защиты от подбора паролей. Оптимально открыть соединение один раз перед циклом, отправить все 20 писем метода Послать() и затем разорвать соединение.
Если логика бизнес-процесса все же требует отправки в рамках одного фонового задания с контролируемым интервалом между сообщениями, необходимо учитывать версию платформы 1С:
ВызватьПаузу(<Интервал>), где интервал задается в миллисекундах. Этот метод усыпляет поток на стороне сервера и не расходует процессорные ресурсы. Например, вызов ВызватьПаузу(3000) сделает паузу в 3 секунды между отправками писем.Пока ТекущаяДата() < ..., так как они загружают ядро процессора сервера на 100%. В таких конфигурациях разделение отправки через регистр сведений и регламентное задание раз в минуту является единственно правильным решением.Если рассылок очень много и не хочется усложнять архитектуру 1С очередями и контролем лимитов, можно применить инфраструктурное решение — развернуть локальный почтовый релей (например, Postfix, Exim или hMailServer):