При реализации массовой рассылки сообщений из 1С разработчики часто сталкиваются с жесткими ограничениями почтовых провайдеров (например, лимит на отправку не более 50 писем в 15 минут). Если отправлять письма в обычном цикле без паузы, почтовый сервер заблокирует учетную запись или начнет отклонять соединения. Рассмотрим подробнее, как корректно организовать задержку выполнения кода в 1С и какие архитектурные подходы позволяют обойти ограничения почтовых служб.
Разберем распространенную ошибку, которую часто допускают начинающие разработчики: создание паузы с помощью циклической проверки времени.
// Неправильный подход!
ДатаНачала = ТекущаяДата();
Пока ТекущаяДата() < ДатаНачала + 30 Цикл
// Пустой цикл ожидания
КонецЦикла;
Проанализируем ситуацию: данный код заставляет процессор выполнять бессмысленные операции на 100% мощности в течение всех 30 секунд. Это приводит к запредельной нагрузке на сервер 1С, зависанию пользовательского сеанса и замедлению работы всех остальных пользователей системы. Пауза должна быть бездеятельной, чтобы освобождать ресурсы сервера.
Один из наиболее чистых способов сделать паузу на сервере без посторонних COM-объектов — запуск фонового задания с последующим ожиданием его завершения через таймаут.
Рассмотрим пример реализации:
Процедура ОтправитьПисьмаСЗадержкой()
// В цикле по письмам
Для Каждого СтрокаПисьма Из ТаблицаПисем Цикл
Попытка
РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(Объект.УчетнаяЗапись, СтрокаПисьма.Параметры);
Исключение
ЗаписьЖурналаРегистрации("Отправка почты", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;
// Организация задержки в 30 секунд
СделатьПаузуЧерезФоновоеЗадание(30);
КонецЦикла;
КонецПроцедуры
Процедура СделатьПаузуЧерезФоновоеЗадание(Секунды)
ПараметрыЗадания = Новый Массив;
// Запускаем фоновое задание с несуществующим или пустым действием
Задание = ФоновыеЗадания.Выполнить("МодульПочтовойРассылки.ПустаяПроцедура", ПараметрыЗадания);
Попытка
// Метод приостанавливает поток на указанное время
Задание.ОжидатьЗавершения(Секунды);
Исключение
// Исключение по истечении таймаута является штатным поведением
КонецПопытки;
КонецПроцедуры
В данном случае метод ОжидатьЗавершения приостанавливает текущий поток без избыточной нагрузки на центральный процессор.
Если сервер 1С или клиентское приложение работают под управлением операционной системы Windows, можно воспользоваться вызовом утилиты sleep через скриптовый объект.
Посмотрим на пример кода:
Процедура СделатьПаузуWScript(Секунды)
#Если Клиент Или СерверНаWindows Тогда
Попытка
scr = Новый COMОбъект("WScript.Shell");
// Метод Run с параметром ожидания (1) блокирует поток на указанное время
scr.Run("timeout /t " + СокрЛП(Число(Секунды)) + " /nobreak", 0, 1);
Исключение
ЗаписьЖурналаРегистрации("Пауза", УровеньЖурналаРегистрации.Предупреждение,,, ОписаниеОшибки());
КонецПопытки;
#КонецЕсли
КонецПроцедуры
Важное ограничение: данный метод неприменим, если сервер 1С работает под управлением ОС Linux, так как COM-объекты там недоступны.
Универсальный способ сделать паузу без нагрузки на процессор, работающий как на Windows, так и на Linux, — попытка подключения к несуществующему локальному адресу с заданным таймаутом.
Процедура ВыполнитьПаузуHTTP(Секунды)
Попытка
// Подключаемся к локальной петле с заведомо неактивным портом
Соединение = Новый HTTPСоединение("127.0.0.1", 65534, , , , Секунды);
Соединение.Получить(Новый HTTPЗапрос("/"));
Исключение
// Перехватываем исключение, которое произойдет ровно через 'Секунды'
КонецПопытки;
КонецПроцедуры
Если ваша конфигурация работает на платформе версии 8.3.25 и выше, разработчики 1С добавили штатный механизм приостановки выполнения кода. Метод ВызватьПаузу доступен на сервере, в фоновых заданиях и сервисах.
// Интервал передается в миллисекундах (30 000 мс = 30 секунд)
ВызватьПаузу(30000);
Использование явных пауз длительностью 30 секунд в интерактивном режиме — не самое оптимальное решение. Если необходимо отправить 100 писем, пользовательский сеанс будет заблокирован почти на час.
Правильный архитектурный подход заключается в разделении процесса на два этапа:
Выясним преимущества такого подхода: