Справочник системных ошибок и решений

Windows Server, Active Directory, 1С:Предприятие, СУБД, Linux, Cisco, MikroTik, Asterisk.

⚠️ Важная информация Все материалы, инструкции, команды и скрипты на сайте предоставлены исключительно в ознакомительных целях. Их применение может повлиять на работу операционной системы, программного обеспечения, баз данных, сетевого оборудования и других компонентов инфраструктуры. Перед выполнением действий создайте резервную копию и по возможности протестируйте изменения в безопасной среде. Пользователь самостоятельно оценивает риски и несет ответственность за результат. При отсутствии необходимых знаний обратитесь к квалифицированному ИТ-специалисту.

1C_MQ_DELIVERY_FAIL 1С:Предприятие и СУБД

Организация очередей сообщений и гарантированной доставки в 1С (RabbitMQ / Native)

Обновлено: 26.08.2026 · Официальная документация ↗

При прямых синхронных обменах между системами возникают проблемы:

  • Потеря пакетов обмена при кратковременной недоступности сервиса-приемника.
  • Зависание пользовательских сеансов при блокировках во время проведения документов, выполняющих синхронные HTTP-запросы.
  • Дублирование документов при повторной отправке одного и того же пакета из-за сетевого таймаута (нарушение идемпотентности).
  • Перегрузка базы-приемника пиковыми всплесками нагрузки (отсутствие Rate Limiting).

1. Архитектурный паттерн Transactional Outbox в 1С

Для обеспечения 100% гарантии доставки без распределенных транзакций используется паттерн Transactional Outbox:

  1. В рамках одной транзакции СУБД проводится бизнес-документ и делается запись в локальную таблицу очередей (РегистрСведений ОчередьИсходящихСообщений).
  2. Отдельное фоновое регламентное задание считывает записи из регистра и отправляет их в брокер сообщений (RabbitMQ / Apache Kafka).
  3. После подтверждения приема брокером (ACK) запись удаляется из очереди.

2. Публикация сообщений в RabbitMQ через HTTP REST API брокера

&НаСервере
Процедура ОтправитьВRabbitMQ(ИмяОчереди, ДанныеСообщенияJSON, ИдентификаторСообщения)
    
    Хост = "rabbitmq.company.ru";
    Порт = 15672; // Порт Management API
    Пользователь = "app_1c";
    Пароль = "RabbitSecretPass";
    VHost = "%2F"; // Дефолтный vhost '/'
    
    HTTPСоединение = Новый HTTPСоединение(
        Хост, Порт, Пользователь, Пароль, , 15, Новый ЗащищенноеСоединениеOpenSSL()
    );
    
    Путь = СтрШаблон("/api/exchanges/%1/amq.default/publish", VHost);
    HTTPЗапрос = Новый HTTPЗапрос(Путь);
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
    
    // Структура публикации RabbitMQ Management API
    ПараметрыПубликации = Новый Структура;
    ПараметрыПубликации.Вставить("properties", Новый Структура("delivery_mode, message_id", 2, ИдентификаторСообщения)); // delivery_mode=2 (Persistent)
    ПараметрыПубликации.Вставить("routing_key", ИмяОчереди);
    ПараметрыПубликации.Вставить("payload", ДанныеСообщенияJSON);
    ПараметрыПубликации.Вставить("payload_encoding", "string");
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, ПараметрыПубликации);
    
    HTTPЗапрос.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть(), КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод("POST", HTTPЗапрос);
    
    Если HTTPОтвет.КодСостояния <> 200 Тогда
        ВызватьИсключение СтрШаблон("Ошибка отправки в очередь (%1): %2", 
            HTTPОтвет.КодСостояния, HTTPОтвет.ПолучитьТелоКакСтроку());
    КонецЕсли;
    
КонецПроцедуры

3. Обработка исходящей очереди регламентным заданием

&НаСервере
Процедура ОбработатьОчередьИсходящихСообщений()
    
    Блокировка = Новый БлокировкаДанных;
    ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ОчередьИсходящихСообщений");
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    
    НачатьТранзакцию();
    Попытка
        Блокировка.Заблокировать();
        
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 100
            |   Очередь.ИдентификаторСообщения КАК ID,
            |   Очередь.ОчередьНазначения КАК Очередь,
            |   Очередь.ТелоСообщения КАК Тело
            |ИЗ
            |   РегистрСведений.ОчередьИсходящихСообщений КАК Очередь
            |УПОРЯДОЧИТЬ ПО
            |   Очередь.МоментВремени ВОЗР";
            
        Выборка = Запрос.Выполнить().Выбрать();
        
        Пока Выборка.Следующий() Цикл
            ОтправитьВRabbitMQ(Выборка.Очередь, Выборка.Тело, Строка(Выборка.ID));
            
            // Удаление успешно отправленного сообщения
            Набор = РегистрыСведений.ОчередьИсходящихСообщений.СоздатьНаборЗаписей();
            Набор.Отбор.ИдентификаторСообщения.Установить(Выборка.ID);
            Набор.Записать();
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("ОчередиСообщений", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры
💡 Практика специалистов: Никогда не выполняйте прямую сетевую отправку в брокер сообщений внутри основной транзакции проведения документа. При сбое сети транзакция откатится, а сообщение уже уйдет в брокер, нарушив консистентность данных.

Частые вопросы (FAQ)

Что такое delivery_mode = 2 в RabbitMQ?

Режим доставки persistent (2) указывает брокеру сохранять сообщение на физический диск, гарантируя его сохранность даже при внезапной перезагрузке ноды RabbitMQ.

Как обеспечить идемпотентность на стороне базы-приемника?

В каждом сообщении передается уникальный UUID (message_id). Приемник перед обработкой проверяет наличие этого UUID в регистре обработанных пакетов; если ID найден, повторная обработка пропускается.

Почему лучше использовать HTTP API RabbitMQ вместо внешних компонент Native AMQP?

HTTP API не требует установки C++ библиотек на серверах Linux/Windows, работает через стандартный объект HTTPСоединение 1С и стабилен в многопоточных фоновых заданиях rphost.

Как предотвратить взаимные блокировки при одновременной записи в очередь Outbox?

Используйте управляемые блокировки (РежимБлокировкиДанных.Управляемый) и записывайте события в очередь через независимый регистр сведений без избыточных измерений.

Полезные материалы
Рекомендуем