В процессе разработки механизмов автоматического оповещения в 1С программисты часто сталкиваются с нестабильностью работы почтовых серверов. Временные разрывы соединения, ограничения антиспам-фильтров (Greylisting) или кратковременная недоступность SMTP-сервера могут привести к потере важных уведомлений. Простого использования конструкции Попытка...Исключение недостаточно, так как она лишь фиксирует факт ошибки, но не решает задачу гарантированной доставки письма. В этой статье мы разберем несколько эффективных способов реализации повторной отправки сообщений, проанализируем их плюсы и минусы, а также изучим особенности работы современных версий платформы 1С:Предприятие.
Самый простой и очевидный способ — обернуть код отправки в цикл. Однако здесь важно соблюсти два правила: ограничить количество итераций, чтобы не войти в бесконечный цикл, и добавить временную паузу между попытками. Рассмотрим пример кода, который реализует такой подход:
МаксимальноеКоличествоПопыток = 5;
ТекущаяПопытка = 1;
УспешноОтправлено = Ложь;
Пока ТекущаяПопытка <= МаксимальноеКоличествоПопыток Цикл
Попытка
Почта.Послать(Сообщение);
УспешноОтправлено = Истина;
Прервать; // Выходим из цикла, если письмо ушло
Исключение
ТекстОшибки = ОписаниеОшибки();
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Попытка " + ТекущаяПопытка + " не удалась: " + ТекстОшибки);
// Делаем паузу перед следующей попыткой
// На сервере 1С нет функции Sleep, используем системные методы или ожидание
Если ТекущаяПопытка < МаксимальноеКоличествоПопыток Тогда
// Например, вызов системной паузы через Ping (простой вариант для сервера)
КомандаПаузы = "ping -n 10 127.0.0.1 > nul";
ЗапуститьПриложение(КомандаПаузы, , Истина);
КонецЕсли;
ТекущаяПопытка = ТекущаяПопытка + 1;
КонецПопытки;
КонецЦикла;
Если Не УспешноОтправлено Тогда
// Обработка ситуации, когда все попытки исчерпаны
ЗаписатьОшибкуВЛог(Сообщение, ТекстОшибки);
КонецЕсли;
Проанализируем этот код. Мы используем переменную ТекущаяПопытка для контроля. Если метод Послать() отрабатывает без исключений, срабатывает оператор Прервать, и выполнение продолжается. Если же возникает ошибка, мы выводим сообщение (или записываем в журнал регистрации) и увеличиваем счетчик. Важно: мгновенные повторы редко помогают при сетевых проблемах, поэтому пауза в 10–30 секунд крайне желательна.
Начиная с версии платформы 8.3.10, поведение метода ИнтернетПочта.Послать() изменилось. Теперь он не всегда вызывает исключение, если письмо не удалось отправить. В некоторых случаях метод возвращает Соответствие, где ключом является адрес получателя, а значением — текст ошибки. Разберем, как правильно обрабатывать такие ситуации:
РезультатОтправки = Почта.Послать(Сообщение);
Если ТипЗнч(РезультатОтправки) = Тип("Соответствие") И РезультатОтправки.Количество() > 0 Тогда
Для Каждого ОшибкаАдреса Из РезультатОтправки Цикл
// Ключ - это e-mail, Значение - текст ошибки
Сообщить("Письмо на адрес " + ОшибкаАдреса.Ключ + " не отправлено. Причина: " + ОшибкаАдреса.Значение);
КонецЦикла;
// Здесь также можно реализовать логику повтора для конкретных адресов
КонецЕсли;
Этот механизм особенно полезен, если в одном письме указано несколько получателей. Если один адрес заблокирован, а другой доступен, 1С отправит письмо доступному адресату и вернет ошибку только по проблемному. В старых версиях (до 8.3.10) метод возвращал Неопределено или вызывал исключение.
Если задача выполняется в фоновом режиме (например, регламентным заданием), мы можем делегировать управление повторами самой платформе 1С. У объекта ФоновыеЗадания при программном запуске есть параметры, отвечающие за поведение при сбоях. Выясним, как это реализовать:
ПараметрыЗадания = Новый Структура;
ПараметрыЗадания.Вставить("КоличествоПовторов", 3);
ПараметрыЗадания.Вставить("ИнтервалПовтора", 60); // пауза в секундах
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ОтправитьПисьмо", ПараметрыПроцедуры, КлючЗадания, , ПараметрыЗадания);
В этом случае внутри процедуры ОтправитьПисьмо нам достаточно просто не перехватывать ошибку через Попытка...Исключение (или использовать ВызватьИсключение в блоке исключения). Если процедура завершится аварийно, платформа сама перезапустит её через указанный интервал. Это наиболее "чистый" архитектурный подход, так как он не блокирует основной поток выполнения и использует штатные возможности системы.
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), мы настоятельно рекомендуем использовать подсистему «Работа с почтовыми сообщениями». Вместо прямой отправки через ИнтернетПочта, следует использовать методы этой подсистемы. Рассмотрим преимущества:
ОчередьПочтовыхСообщений.Пример программного добавления в очередь (схематично для БСП):
ПараметрыПисьма = Новый Структура;
ПараметрыПисьма.Вставить("Кому", "owner@example.com");
ПараметрыПисьма.Вставить("Тема", "Отчет по копии ИБ");
ПараметрыПисьма.Вставить("Тело", "Текст сообщения...");
// Вызов стандартного метода БСП
РаботаСПочтой.ОтправитьПочтовоеСообщение(УчетнаяЗапись, ПараметрыПисьма);
При реализации механизмов повтора важно учитывать природу возникающих ошибок. Проанализируем ситуации, в которых повтор может быть вреден:
НачатьТранзакцию() и ЗафиксироватьТранзакцию()). Если после отправки письма произойдет сбой в базе данных и транзакция откатится, письмо уже уйдет получателю, что приведет к рассогласованию данных. Сначала фиксируйте изменения в БД, а затем инициируйте отправку почты.Подводя итог, выбор метода зависит от критичности сообщения и архитектуры вашей системы. Для простых обработок подойдет цикл со счетчиком и паузой, для серверных решений — фоновые задания с настройкой повторов, а для крупных корпоративных систем — использование очереди сообщений в БСП.