При сопровождении конфигураций на обычных формах (например, 1С:Управление торговлей 10.3) в файловом режиме работы разработчики нередко сталкиваются с ситуацией: регламентное или фоновое задание отрабатывает полностью, в журнале регистрации фиксируются события Фоновое задание.Успешное завершение и Сеанс.Завершение, однако сразу после этого сеанс 1С с открытой обработкой «Консоль заданий» намертво зависает и перестает отвечать на запросы операционной системы.
Проанализируем детально, почему возникает подобное поведение платформы 1С, как на это влияет объект ИнтернетПочта при пересылке больших файлов и какими способами можно полностью устранить эту проблему.
Выясним, какие механизмы приводят к блокировке пользовательского интерфейса и системных потоков при выполнении фоновой рассылки почты в файловой базе:
ИнтернетПочта и дедлок сокетов. При отправке больших файлов соединение по протоколам SMTP/TLS остается активным длительное время. Если в программном коде не вызывается явный метод Отключиться(), освобождение ресурсов и закрытие сокетов выполняются платформой неявно при удалении объекта из памяти. В 32-разрядных процессах Windows сетевой стек на уровне библиотек шифрования (OpenSSL или SChannel) может удерживать сокет в состоянии TIME_WAIT, вызывая взаимную блокировку (deadlock) системного потока при завершении фонового задания.ФоновыеЗадания.ПолучитьФоновыеЗадания(). Если дочерний поток завершился логически, но заблокирован на уровне освобождения сетевых дескрипторов (handles), вызов метода получения заданий блокирует основной интерфейсный поток 1С.%TEMP%. В условиях 32-битной архитектуры сборщик мусора 1С при выгрузке фонового потока может аварийно зацикливаться на освобождении больших непрерывных блоков памяти.Разберем, как правильно организовать программный код отправки сообщений, чтобы гарантировать корректное закрытие TCP-сокета и своевременное освобождение оперативной памяти.
Главное правило: вызовы Подключиться() и Отключиться() должны быть строго сбалансированы и обрамлены блоком Попытка ... Исключение, а для сетевого профиля должен быть установлен достаточный таймаут ожидания.
Посмотрим на пример правильной реализации процедуры отправки почты:
Процедура ОтправитьПисьмоСФайлом(Профиль, ПочтовоеСообщение) Экспорт
Почта = Новый ИнтернетПочта;
// Задаем явный таймаут для предотвращения бесконечного ожидания ответа сервера
Профиль.ВремяОжидания = 60;
Попытка
Почта.Подключиться(Профиль);
Почта.Послать(ПочтовоеСообщение);
// Делаем небольшую технологическую паузу для завершения обмена пакетами
Приостановить(1);
Почта.Отключиться();
Исключение
// Гарантированно закрываем сокет при возникновении любой ошибки
Попытка
Почта.Отключиться();
Исключение
// Игнорируем ошибку отключения, если соединение уже разорвано
КонецПопытки;
ЗаписьЖурналаРегистрации("ОтправкаПочты", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
ВызватьИсключение;
КонецПопытки;
// Принудительно очищаем ссылки на тяжелые объекты до завершения фонового потока
ПочтовоеСообщение = Неопределено;
Почта = Неопределено;
КонецПроцедуры
Рассмотрим, что изменилось в коде:
ВремяОжидания = 60 у почтового профиля, что исключает зависание при сбоях на стороне SMTP-сервера.Почта.Отключиться() вызывается явно как в штатном режиме, так и в блоке обработки исключений.Приостановить(1), позволяющая операционной системе корректно обработать закрытие TCP-соединения перед завершением потока.ПочтовоеСообщение и Почта, что позволяет сборщику мусора очистить память до завершения сеанса фонового задания.При формировании вложений платформа сохраняет промежуточные файлы в системном временном каталоге. Если они не удаляются сразу после отправки, это приводит к блокировке дескрипторов файлов процессом 1cv8.exe.
Посмотрим, как организовать корректное удаление временных вложений:
ВременныйФайл = ПолучитьИмяВременногоФайла("zip");
// ... формирование и сохранение архива во ВременныйФайл ...
ПочтовоеСообщение = Новый ИнтернетПочтовоеСообщение;
ПочтовоеСообщение.Вложения.Добавить(ВременныйФайл, "Отчет.zip");
ОтправитьПисьмоСФайлом(ПочтовыйПрофиль, ПочтовоеСообщение);
// Удаляем временный файл сразу после завершения отправки
Попытка
УдалитьФайлы(ВременныйФайл);
Исключение
ЗаписьЖурналаРегистрации("ОчисткаВременныхФайлов", УровеньЖурналаРегистрации.Предупреждение,,, ОписаниеОшибки());
КонецПопытки;
Если специфика файловой базы и особенности почтового сервера все равно приводят к дедлокам платформенного сетевого стека при отправке файлов объемом в десятки мегабайт, наиболее стабильным решением является делегирование отправки внешней системной утилите (например, curl или скрипту PowerShell).
В этом случае платформа 1С освобождается от необходимости удерживать сетевые сокеты и обрабатывать тяжелые бинарные буферы в своей памяти.
Разберем пример отправки письма через запуск скрипта PowerShell из 1С:
Процедура ОтправитьПисьмоЧерезВнешнийПроцесс(Сервер, Порт, Пользователь, Пароль, Кому, Тема, ПутьКФайлу)
КомандаPowerShell = СтрШаблон(
"powershell -ExecutionPolicy Bypass -Command """ +
"$secpasswd = ConvertTo-SecureString '%1' -AsPlainText -Force; " +
"$creds = New-Object System.Management.Automation.PSCredential ('%2', $secpasswd); " +
"Send-MailMessage -From '%2' -To '%3' -Subject '%4' -Attachments '%5' -SmtpServer '%6' -Port %7 -UseSsl -Credential $creds;""",
Пароль, Пользователь, Кому, Тема, ПутьКФайлу, Сервер, Формат(Порт, "ЧГ=0")
);
// Запускаем процесс в скрытом режиме с ожиданием завершения
КодВозврата = 0;
ЗапуститьПриложение(КомандаPowerShell, , Истина, КодВозврата);
Если КодВозврата <> 0 Тогда
ВызватьИсключение "Ошибка отправки почты через внешний процесс. Код ошибки: " + Строка(КодВозврата);
КонецЕсли;
КонецПроцедуры
Проанализируем дополнительные системные настройки, которые необходимо выполнить на компьютере, где выполняются регламентные задания:
1cv8.exe и системный каталог временных файлов пользователя (%TEMP%) в белый список антивирусного монитора./Execute <ПутьКОбработке> и последующим программным вызовом ЗавершитьРаботуСистемы(Ложь).