Подготовка окружения
Компонента kovalevdmv/1CRabbitMQ, обработка КлиентRMQ. Брокер - как в части 1.
Код примеров собран в расширении RMQ_lessons (malikov-pro/1CRabbitMQ) для запуска через YAxUnit.
Те же сценарии можно вызывать из консоли кода в серверном контексте - например bsl_console и аналоги.
На чём сосредоточена часть
В части 2 мы помечали сообщения как persistent и объявляли durable-очередь - это про хранение после того, как брокер сообщение принял. Publisher confirms отвечают на другой вопрос: брокер подтвердил приём публикации?
Это расширение AMQP 0-9.1: на канале с confirms клиент публикует, сервер асинхронно шлёт ack/nack. Клиентские библиотеки по-разному оборачивают ожидание.
В КлиентRMQ доступна синхронная публикация с confirms. Ниже - этот режим в коде и кратко два других подхода (пакет / async), которые в компоненте «из коробки» не оформлены.
Подходы к confirms
Публикация по одному (доступна в 1С)
В КлиентRMQ confirms включают вторым параметром СоздатьКанал. После ОпубликоватьСообщение в Ответ.Текст уже есть статус (is_ack / is_nack / not_requested). Без флага на канале статус confirms не приходит.
Подключение = КлиентRMQ.ПодключитьсяКСерверу(URI);
ПодтверждениеПубликацийОтСервера = Истина;
Канал = КлиентRMQ.СоздатьКанал(Подключение, ПодтверждениеПубликацийОтСервера);
ИмяОчереди = ОМ_РМКУ_Настройки.ИмяОчередиЧасть7(); // "confirm_demo"
Очередь = ОМ_РМКУ_Настройки.ОбъявитьОчередь(КлиентRMQ, Канал, ИмяОчереди); // durable + quorum
Ответ = КлиентRMQ.ОпубликоватьСообщение(
Канал, ТекстСообщения, ИмяОчереди, "", Истина, Истина); // persistent
Если КлиентRMQ.ЭтоОшибка(Ответ) Тогда
ВызватьИсключение Ответ.Текст;
КонецЕсли;
Если Ответ.Текст = КлиентRMQ.ПодтверждениеСервера_СерверПодтвердилПолучениеСообщения() Тогда
// брокер принял
ИначеЕсли Ответ.Текст = КлиентRMQ.ПодтверждениеСервера_Сервер_НЕ_ПодтвердилПолучениеСообщения() Тогда
ВызватьИсключение "Публикация отклонена (nack)";
Иначе
// not_requested - забыли включить confirms на канале
КонецЕсли;
Очередь confirm_demo - именованная durable + quorum, как в частях 1–2: тип очереди на смысл confirms не влияет.
Плюс: просто - опубликовали и сразу проверили статус. Минус: каждое сообщение ждёт свой круг подтверждения; для умеренной нагрузки обычно хватает, при высокой - узкое место.
Пакетная публикация (batch)
Клиент публикует несколько сообщений подряд без ожидания ack после каждого, затем один раз ждёт подтверждения на всю пачку (или до определённого sequence number).
Преимущество: заметно выше throughput - круг ожидания делится на N сообщений, а не повторяется N раз. Цена: при nack или обрыве труднее понять, какое сообщение из пачки не принято; обычно переигрывают всю пачку или опираются на идемпотентность получателя.
Асинхронные confirms
Клиент не блокируется на публикации: брокер шлёт ack/nack в фоне, библиотека вызывает callback и ведёт учёт по sequence number (какие delivery ещё «в полёте»).
Преимущество: максимальная скорость при сохранении надёжности - канал загружен публикациями, подтверждения обрабатываются по мере поступления. Цена: больше кода (карта неподтверждённых, таймауты, повтор при nack), сложнее рассуждать о порядке и корректности при ошибках.
Отказоустойчивость и производительность
Confirms, durable/persistent и consumer ack отвечают на разные сбои - их не стоит смешивать в одну «гарантию».
Приём на брокере. Publisher confirms говорят: сервер принял публикацию на канал. Без них клиент не знает, дошло ли сообщение после обрыва сети или таймаута. Это слой «отправили не в пустоту».
Переживание рестарта. Durable-очередь и persistent-сообщение повышают шанс, что принятое сообщение останется на диске после рестарта брокера. Confirms этого не заменяют: is_ack не значит «уже записано навечно», а durable без confirms не отвечает, успела ли публикация дойти.
Обработка у получателя. Ручной ack (часть 2) - про то, что исполнитель завершил работу. Сообщение могло лежать в Ready/Unacked, пока consumer не подтвердил; падение исполнителя без ack возвращает задачу в очередь.
Производительность. Синхронный confirm на каждое сообщение - самый простой и самый дорогой по latency: круг «publish U94; wait ack» на каждое тело. Пакет и async (выше) поднимают throughput ценой сложности при nack. Persistent + quorum тоже дороже transient/classic: больше записи на диск и репликация. Имеет смысл включать слои там, где цена потери выше цены задержки: confirms на критичной публикации, durable/persistent на данных, которые нельзя потерять при рестарте, ручной ack на работе, которую нельзя «съесть» дважды без идемпотентности.
На практике слои комбинируют: confirms при отправке + persistent в durable (quorum) очередь + ack у исполнителя. «Ровно один раз навсегда» ни один слой сам не даёт - см. документацию confirms.
Собираем вместе
КлиентRMQ = Обработки.КлиентRMQ.Создать();
Подключение = КлиентRMQ.ПодключитьсяКСерверу(URI);
Канал = КлиентRMQ.СоздатьКанал(Подключение, Истина); // confirms ON
ИмяОчереди = ОМ_РМКУ_Настройки.ИмяОчередиЧасть7(); // "confirm_demo"
ОМ_РМКУ_Настройки.ОбъявитьОчередь(КлиентRMQ, Канал, ИмяОчереди);
Для Номер = 1 По 10 Цикл
Ответ = КлиентRMQ.ОпубликоватьСообщение(
Канал, "msg-" + Номер, ИмяОчереди, "", Истина, Истина);
Если Ответ.Текст <> КлиентRMQ.ПодтверждениеСервера_СерверПодтвердилПолучениеСообщения() Тогда
ВызватьИсключение СтрШаблон("Нет confirm для %1: %2", Номер, Ответ.Текст);
КонецЕсли;
КонецЦикла;
КлиентRMQ.ЗакрытьКанал(Канал);
КлиентRMQ.ОтключитьсяОтСервера(Подключение);
Полный код и замеры - в расширении (по желанию).
Запуск решения
Если брокер помнит confirm_demo с другими параметрами - удалите через ОМ_РМКУ_ManagementAPI.УдалитьОчередь (или UI) и объявите снова (см. часть 1).
- Канал с
ПодтверждениеПубликацийОтСервера = Истина. - Опубликовать в
confirm_demo(ОбъявитьОчередь- durable + quorum). - Убедиться, что
Ответ.Текст = is_ack. - Для контраста: канал без confirms U94;
not_requested.
YAxUnit (набор «Урок 7»): ОМ_ТестRMQ_Lessons.Урок7_PublisherConfirms.
В ЖР / окне сообщений примерно:
… | Отправка в очередь | … | данные: confirm:…
… | Подтверждение сервера | … | данные: is_ack
Результат
Включили publisher confirms на канале КлиентRMQ, проверили синхронный ack/nack, отделили confirms от durable/persistent и от consumer ack.
Отдельно - часть 8 и часть 9: очереди типа stream.
Ссылки на остальные части
- Часть 1. Hello World
- Часть 2. Work Queues - рабочие очереди
- Часть 3. Publish/Subscribe - одно сообщение многим (fanout)
- Часть 4. Routing - маршрутизация по ключу (direct)
- Часть 5. Topics - маршрутизация по шаблону
- Часть 6. RPC - удалённый вызов процедур
- Часть 7. Publisher Confirms - надёжная публикация (эта статья)
- Часть 8. Streams - Hello World
- Часть 9. Streams - отслеживание смещения
Благодарю за внимание.
Создано совместно с Cursor Grok 4.5
Вступайте в нашу телеграмм-группу Инфостарт