MS SQL Server Service Broker: углублённый курс

Курс для глубокого понимания архитектуры и внутренней работы Service Broker в SQL Server. Рассматриваются проектирование решений, надёжность доставки, безопасность, эксплуатация, диагностика и производительность на реальных сценариях.

1. Архитектура Service Broker и ключевые понятия

Архитектура Service Broker и ключевые понятия

Service Broker (SB) — встроенный в Microsoft SQL Server механизм асинхронного обмена сообщениями и построения надёжных очередей внутри SQL Server и между экземплярами SQL Server. Его часто используют для фоновой обработки, интеграции модулей, разгрузки онлайн-транзакций, построения событийных процессов.

В этой статье мы разберём архитектуру Service Broker и термины, без которых невозможно уверенно читать документацию и проектировать решения на SB.

> Service Broker is a feature that provides native support for messaging and queuing in the Microsoft SQL Server Database Engine. — Microsoft Learn, раздел про Service Broker Service Broker

Когда Service Broker уместен

Service Broker решает задачи, где важны надёжность и развязка по времени между производителем и потребителем.

Маркерные сценарии:

  • Фоновая обработка: онлайн-транзакция быстро фиксируется, тяжёлая работа уходит в очередь.
  • Интеграция модулей: один компонент отправляет сообщения, другой забирает и обрабатывает.
  • Межбазовое взаимодействие: обмен между базами на одном экземпляре или между экземплярами.
  • Управляемая конкуренция: несколько воркеров читают из одной очереди, сохраняя транзакционность.
  • Ограничения, которые важно знать заранее:

  • Service Broker — это не «просто таблица-очередь». У него есть собственный протокол диалогов, системные очереди и механизмы доставки.
  • Межсерверный обмен требует сетевой настройки (эндпоинты, маршруты) и часто настройки безопасности.
  • Общая архитектура: из каких слоёв состоит Service Broker

    В Service Broker удобно мыслить слоями: модель сообщенийдиалогихранение и доставкаобработка.

    !Общая карта объектов и поток сообщения от отправителя к получателю

    Ключевая идея: приложение не пишет напрямую в очередь получателя. Оно отправляет сообщение в рамках диалога между сервисами, а Service Broker гарантирует доставку и порядок в пределах диалога.

    Базовые сущности Service Broker

    Ниже — «словарь» объектов. Эти сущности создаются DDL-командами и существуют как объекты базы данных.

    Сообщение

    Сообщение — единица данных, которая передаётся между сервисами.

    Что важно:

  • Сообщение имеет тип.
  • Тело сообщения — это бинарные данные. Часто туда кладут XML или JSON, но SB не «понимает» их семантику, он гарантирует доставку.
  • Тип сообщения (Message Type)

    Message Type задаёт правила валидации тела сообщения.

    Популярные варианты:

  • WELL_FORMED_XML — сообщение должно быть корректным XML.
  • NONE — без валидации (быстрее, но контроль на приложении).
  • Документация: CREATE MESSAGE TYPE

    Контракт (Contract)

    Contract описывает, какие типы сообщений разрешены в диалоге и кто имеет право их отправлять.

    Почему это важно:

  • Контракт — это «API» диалога.
  • Он предотвращает ситуации, когда сторона отправляет неожиданные сообщения.
  • Документация: CREATE CONTRACT

    Очередь (Queue)

    Queue — хранилище сообщений в базе данных. Физически это внутренний объект, но работать с ним можно через RECEIVE.

    Свойства очереди, которые нужно помнить:

  • Очередь может иметь активацию (автозапуск обработчиков).
  • Очередь участвует в транзакциях: получение сообщения можно коммитить или откатывать.
  • Документация: CREATE QUEUE

    Сервис (Service)

    Service — логическая конечная точка SB в базе данных. Сервис привязан к одной очереди и набору контрактов, которые он поддерживает.

    Интуитивная модель:

  • Очередь — это почтовый ящик.
  • Сервис — это адрес и правила общения.
  • Документация: CREATE SERVICE

    Диалог (Conversation)

    Conversation — долгоживущий двусторонний канал между двумя сервисами в рамках конкретного контракта.

    Свойства диалога:

  • У диалога есть идентификатор conversation_handle.
  • Сообщения в рамках одного диалога доставляются в порядке отправки.
  • Диалог нужно корректно завершать.
  • Технически диалог живёт в системном состоянии (например, в представлениях sys.conversation_endpoints).

    Документация: BEGIN DIALOG CONVERSATION

    Конечные системные сообщения

    Service Broker использует два стандартных сообщения, которые встречаются почти в каждом решении:

  • EndDialog — корректное завершение диалога.
  • Error — сигнал об ошибке на уровне диалога.
  • Эти сообщения нужно обрабатывать, иначе диалоги будут «зависать».

    Как сообщение проходит путь от отправителя к получателю

    Полезно запомнить цепочку действий на концептуальном уровне.

  • Отправитель открывает диалог (создаёт состояние диалога) командой BEGIN DIALOG CONVERSATION.
  • Отправитель посылает сообщение командой SEND ON CONVERSATION.
  • Service Broker помещает сообщение в локальные структуры доставки.
  • Если получатель в той же базе, сообщение попадает в очередь получателя.
  • Если получатель удалённый, сообщение сначала попадает в очередь передачи (transmission queue) и ждёт доставки по маршруту.
  • Получатель забирает сообщение командой RECEIVE (обычно в транзакции).
  • Стороны обмениваются сообщениями, пока не завершат диалог END CONVERSATION.
  • Документация: SEND, RECEIVE, END CONVERSATION

    Надёжность и транзакционность: почему SB не «теряет» сообщения

    Service Broker тесно интегрирован с транзакциями SQL Server.

    Практическая интерпретация:

  • Отправка сообщения может быть частью транзакции с обычными DML-операциями.
  • Если транзакция откатывается, отправленное сообщение не считается отправленным.
  • Получение (RECEIVE) тоже транзакционно: если обработчик упал и транзакция откатилась, сообщение вернётся в очередь.
  • Это основа паттерна exactly-once processing на уровне очереди при корректной логике обработчика.

    Встроенная обработка: активация очереди (Activation)

    Activation — механизм, при котором SQL Server сам запускает обработчики сообщений (хранимые процедуры) при появлении сообщений в очереди.

    Зачем это нужно:

  • Не держать внешний сервис-пуллер.
  • Автоматически масштабировать количество обработчиков.
  • Ключевые элементы активации:

  • ACTIVATION (STATUS = ON, PROCEDURE_NAME = ..., MAX_QUEUE_READERS = N, EXECUTE AS ...) в настройках очереди.
  • MAX_QUEUE_READERS управляет параллелизмом (сколько одновременных экземпляров обработчика может читать очередь).
  • Документация: раздел про активацию в CREATE QUEUE

    Маршрутизация и сетевое взаимодействие

    Когда сервисы находятся в разных базах или на разных экземплярах SQL Server, в архитектуре появляются дополнительные сущности.

    Маршрут (Route)

    Route связывает имя удалённого сервиса с адресом доставки (куда отправлять сообщения).

    Документация: CREATE ROUTE

    Endpoint

    Service Broker endpoint — сетевой слушатель, который принимает и отправляет SB-трафик между экземплярами.

    Документация: CREATE ENDPOINT

    Транспортная очередь (Transmission Queue)

    Transmission queue — внутренняя очередь, где накапливаются сообщения, ожидающие отправки на удалённый сервис.

    Зачем она важна при диагностике:

  • Если удалённый сервер недоступен или маршрут/безопасность настроены неверно, сообщения «застревают» здесь.
  • На практике её смотрят через системные представления и DMV (в следующих статьях курса мы разберём диагностику подробно).

    Группы диалогов и конкуренция

    Группа диалога (Conversation Group)

    Conversation group — механизм, который позволяет обрабатывать связанные диалоги согласованно, не допуская параллельной обработки конфликтующих сообщений разными воркерами.

    Практический смысл:

  • Если вы хотите, чтобы все сообщения по одному бизнес-ключу обрабатывались строго последовательно, вы часто привязываете их к одной группе.
  • В обработчиках это проявляется через блокировки на группу при RECEIVE.

    Безопасность: что защищается и чем

    В Service Broker есть два уровня безопасности:

  • Локальная безопасность: кто может отправлять/получать в пределах базы (права на сервис/очередь).
  • Диалоговая безопасность: аутентификация и шифрование между сервисами (особенно важно для межсерверного обмена).
  • Минимальная практическая рекомендация:

  • Для межсерверных сценариев заранее планировать модель сертификатов/пользователей, иначе проект упрётся в ошибки доставки.
  • Обзор: Security for Service Broker

    Жизненный цикл диалога и утечки

    Диалог — это состояние, которое нужно закрывать.

    Типовые причины «утечек диалогов»:

  • Не обрабатывается сообщение EndDialog.
  • Ошибка на одной стороне не приводит к END CONVERSATION на другой.
  • Логика обработчика не завершает диалог после выполнения «одноразовой» задачи.
  • Результат:

  • Растёт системное состояние диалогов.
  • Диагностика показывает большое число активных endpoints.
  • Команды завершения: END CONVERSATION

    «Ядовитые» сообщения и повторные доставки

    Если обработчик получает сообщение, падает и откатывает транзакцию, то сообщение вернётся в очередь и будет доставлено снова. Это правильно, но может превратиться в бесконечный цикл.

    Такое сообщение часто называют poison message.

    Что делают в зрелых решениях:

  • В обработчике вводят счётчик попыток (например, в теле сообщения или отдельной таблице состояния).
  • При превышении лимита отправляют сообщение в «карантинную» очередь или логируют для ручного разбора.
  • Следят за тем, чтобы обработчик был идемпотентным (повтор не ломает данные).
  • Минимальная карта объектов и где их наблюдать

    В Service Broker полезно уметь быстро сопоставлять DDL-объекты и системные представления.

    | Сущность | Создаётся чем | Где смотреть состояние/метаданные | |---|---|---| | Message Type | CREATE MESSAGE TYPE | sys.service_message_types | | Contract | CREATE CONTRACT | sys.service_contracts | | Queue | CREATE QUEUE | sys.service_queues | | Service | CREATE SERVICE | sys.services | | Route | CREATE ROUTE | sys.routes | | Conversation endpoints | BEGIN DIALOG CONVERSATION (создаёт состояние) | sys.conversation_endpoints |

    Справочник системных представлений: Service Broker Catalog Views

    Подготовка базы: включён ли Broker

    Service Broker включается на уровне базы данных. Если он выключен, многие операции будут завершаться ошибками или не дадут ожидаемого поведения.

    Проверка и включение описаны в документации по ALTER DATABASE:

  • SET ENABLE_BROKER
  • Документация: ALTER DATABASE SET Options

    Итоги

    В Service Broker архитектура строится вокруг нескольких опорных идей:

  • Сервис + очередь — адрес и почтовый ящик.
  • Контракт + типы сообщений — формализованный протокол.
  • Диалог — упорядоченный и надёжный канал, который нужно корректно закрывать.
  • Транзакционность — основа надёжной отправки и получения.
  • Активация — встроенный механизм запуска обработчиков.
  • Маршруты и эндпоинты — основа межсерверного обмена.
  • В следующей статье логично перейти от терминов к скелету рабочей конфигурации: развернуть минимальный локальный пример (message type → contract → queue → service), отправить сообщения и разобрать, как писать обработчик с RECEIVE и корректным завершением диалогов.

    2. Объекты и конфигурация: message types, contracts, queues, services

    Объекты и конфигурация: message types, contracts, queues, services

    В предыдущей статье мы разобрали архитектуру Service Broker (SB): сообщения идут не “прямо в очередь”, а через диалоги между сервисами, по правилам контракта, с валидацией по типам сообщений.

    В этой статье мы соберём минимально правильную локальную конфигурацию SB (в пределах одной базы) и разберём, как настраивать ключевые объекты:

  • MESSAGE TYPE
  • CONTRACT
  • QUEUE
  • SERVICE
  • Параллельно закрепим практические нюансы: включение SB в базе, активация очереди, права, и что именно проверять после развёртывания.

    !Карта связей: типы сообщений → контракт → сервисы → очереди и базовый путь сообщения

    Предварительные условия

    Service Broker должен быть включён в базе

    SB включается на уровне базы. Если он выключен, вы столкнётесь с ошибками при попытке диалогов и отправки.

    Проверка:

    Включение (осторожно на продакшене: может потребовать эксклюзивного доступа к базе):

    Документация: ALTER DATABASE SET ENABLE_BROKER

    Принципы, которые помогут избежать проблем

  • Делайте имена объектов явно связанными с бизнес-сценарием (например, Order_, Billing_).
  • На первых шагах используйте локальный сценарий в одной базе: меньше сетевых и крипто-настроек.
  • Сразу планируйте обработку системных сообщений EndDialog и Error, иначе “утекут” диалоги.
  • Message Type: типы сообщений и валидация

    MESSAGE TYPE определяет “класс” сообщения и политику валидации тела.

    Ключевые опции валидации:

  • NONE — SB не валидирует тело сообщения.
  • WELL_FORMED_XML — тело должно быть корректным XML.
  • Важно:

  • SB хранит тело как бинарные данные, но при WELL_FORMED_XML проверяет корректность XML.
  • Валидация — это баланс надёжности и стоимости: NONE быстрее, но контроль полностью на вашей стороне.
  • Документация: CREATE MESSAGE TYPE

    Пример

    Создадим два типа: запрос в XML и ответ без валидации.

    Рекомендации по именованию:

  • Часто используют URI-подобный стиль (//company/app/MessageType) — это удобно, потому что имена должны быть уникальными в пределах базы.
  • Contract: протокол диалога и кто что может отправлять

    CONTRACT задаёт правила: какие типы сообщений допустимы в диалоге и какая сторона имеет право их отправлять.

    Стороны:

  • INITIATOR — сторона, которая обычно начинает диалог.
  • TARGET — принимающая сторона.
  • Почему это важно:

  • Контракт — это “жёсткий API”: если отправить тип сообщения, не разрешённый контрактом, SB вернёт ошибку диалога.
  • Контракт помогает дисциплинировать взаимодействие и упрощает сопровождение.
  • Документация: CREATE CONTRACT

    Пример

    Разрешим инициатору отправлять запрос, а таргету — ответ.

    Queue: очередь сообщений и настройка обработки

    QUEUE — объект хранения сообщений, из которого читают через RECEIVE. Очередь транзакционна: если вы получили сообщение, но откатили транзакцию, оно вернётся в очередь.

    Документация: CREATE QUEUE

    Ключевые настройки очереди

  • STATUS = ON|OFF — включена ли очередь.
  • ACTIVATION — автозапуск обработчиков.
  • MAX_QUEUE_READERS — максимальное число параллельных обработчиков.
  • EXECUTE AS — контекст безопасности, от которого запускается процедура активации.
  • Практические замечания:

  • При активации обработчик должен быть написан так, чтобы корректно работал при параллельном запуске (если MAX_QUEUE_READERS > 1).
  • “Ядовитые сообщения” (ошибка обработки с постоянным откатом) часто проявляются именно в активации, поэтому обработчик должен аккуратно логировать и завершать диалоги.
  • Пример: очереди без активации (сначала проще)

    На первом шаге удобно создать очереди без активации и проверить ручным RECEIVE.

    Service: конечная точка, привязанная к очереди

    SERVICE — логическая точка общения. Сервис всегда привязан к одной очереди. Сервис также объявляет, какие контракты он поддерживает.

    Документация: CREATE SERVICE

    Пример

    Создадим сервис инициатора и сервис получателя. Получатель явно указывает контракт; инициатор может также перечислять контракты (хорошая практика — указывать явно).

    Минимальный рабочий пример: открыть диалог, отправить, получить

    Ниже — “скелет” рабочего сценария в одной базе. Это центральная проверка того, что объекты созданы и связаны правильно.

    Документация команд: BEGIN DIALOG CONVERSATION, SEND, RECEIVE, END CONVERSATION

    Отправка сообщения (инициатор)

    Замечание про WITH ENCRYPTION = OFF:

  • В локальной базе это удобно для простоты.
  • Для межсерверных сценариев шифрование и безопасность нужно планировать отдельно.
  • Получение на стороне таргета (ручной RECEIVE)

    Почему важно завершать диалог на таргете:

  • Если это “одноразовый” запрос-ответ, таргет обычно закрывает свою сторону после ответа.
  • Инициатор позже получит системное сообщение EndDialog и тоже должен закрыть диалог у себя.
  • Получение ответа инициатором и корректное закрытие

    Практический смысл обработки EndDialog и Error:

  • Если их игнорировать, будут накапливаться “живые” endpoints в sys.conversation_endpoints.
  • Активация очереди: подключаем обработчик

    Ручной RECEIVE полезен для обучения и диагностики, но в реальных системах чаще используют активацию: при появлении сообщений SQL Server сам запускает процедуру-воркер.

    Пример процедуры-обработчика (каркас)

    Важно: это каркас, который показывает ключевые элементы: цикл, транзакция, обработка системных сообщений, завершение диалогов.

    Технические детали, которые здесь принципиальны:

  • WAITFOR (RECEIVE ...) TIMEOUT позволяет воркеру “дожидаться” сообщений и корректно завершаться при пустой очереди.
  • BEGIN TRAN ... COMMIT обеспечивает транзакционность: если обработка упадёт и транзакция откатится, сообщение вернётся в очередь.
  • Включение активации на очереди

    Рекомендации по MAX_QUEUE_READERS:

  • Начинайте с небольших значений и увеличивайте после измерений.
  • Если порядок обработки важен по бизнес-ключу, параллелизм нужно проектировать отдельно (например, через conversation groups), иначе вы получите гонки.
  • Права и доступ: минимальная модель

    На локальном демо часто работают под dbo, но в реальных системах права важны.

    Что обычно требуется:

  • Отправителю нужны права на сервис, из которого он начинает диалог.
  • Получателю нужны права читать очередь (RECEIVE) и выполнять обработчик.
  • На практике чаще делают так:

  • Процедура активации запускается EXECUTE AS OWNER.
  • Приложение получает минимальные права на отправку (например, через подписанную процедуру-обёртку, которая делает BEGIN DIALOG и SEND).
  • Документация по безопасности: Security for Service Broker

    Проверка конфигурации: что смотреть в системных представлениях

    После создания объектов удобно проверить метаданные и состояние.

    Метаданные объектов

    Документация: Service Broker Catalog Views

    Состояние диалогов

    Если вы видите много диалогов в состоянии, которое “не заканчивается”, почти всегда причина в том, что приложение не обрабатывает EndDialog/Error или забывает делать END CONVERSATION.

    Частые ошибки конфигурации и как их распознать

  • Контракт не разрешает сообщение от этой стороны.
  • Сервис не поддерживает контракт.
  • Очередь выключена (STATUS = OFF) или активация выключена.
  • Процедура активации не существует или не имеет прав выполняться.
  • Приложение не завершает диалоги и копит состояние.
  • Минимальная стратегия диагностики:

  • Проверить наличие объектов в sys.service_message_types, sys.service_contracts, sys.services, sys.service_queues.
  • Проверить is_activation_enabled и max_readers для очередей.
  • Проверить sys.conversation_endpoints на “зависшие” диалоги.
  • Итоги

    Вы собрали базовую конфигурацию Service Broker в одной базе и увидели, как связаны ключевые объекты:

  • MESSAGE TYPE определяет допустимое тело сообщения.
  • CONTRACT описывает протокол и направления сообщений.
  • QUEUE хранит сообщения и задаёт модель обработки (включая активацию).
  • SERVICE связывает внешний “адрес” SB с конкретной очередью и контрактами.
  • Дальше в курсе логично углубляться в диалоги и обработку: как строить устойчивые воркеры, как правильно масштабировать параллелизм, как диагностировать проблемы доставки и “ядовитые” сообщения.

    3. Диалоги и обмен сообщениями: conversation groups и порядок доставки

    Диалоги и обмен сообщениями: conversation groups и порядок доставки

    В предыдущих статьях курса мы собрали минимальную конфигурацию Service Broker: типы сообщенийконтрактыочередисервисы, а затем отправили и получили сообщения через диалог.

    Следующий шаг углубления — понять, какие именно гарантии даёт Service Broker при доставке сообщений, где заканчивается гарантия порядка, и как проектировать обработчики так, чтобы при параллельной обработке не возникало гонок. Для этого нужно уверенно владеть двумя понятиями:

  • Диалог (conversation) как граница гарантий порядка.
  • Группа диалогов (conversation group) как механизм сериализации обработки связанных сообщений.
  • !Иллюстрация границ порядка (внутри диалога) и блокировки по conversation group при параллельных воркерах

    Что гарантирует Service Broker и где именно

    Порядок доставки

    Service Broker гарантирует, что сообщения в рамках одного диалога доставляются получателю в том порядке, в котором были отправлены (при условии успешной доставки). Это ключевая гарантия, на которую опираются протоколы request-reply, команды-события и многие фоновые пайплайны.

    Важно понимать границу:

  • Порядок гарантирован только внутри одного conversation_handle.
  • Между разными диалогами порядок не гарантируется.
  • Если вы отправили два сообщения в разных диалогах, получатель может получить их в любом взаимном порядке, особенно при параллельной обработке и нагрузке.
  • Документация команд, вокруг которых строится обмен:

  • BEGIN DIALOG CONVERSATION
  • SEND
  • RECEIVE
  • Ровно-один-раз и транзакционность получения

    На практике важна связка: порядок в диалоге + транзакционность RECEIVE.

    Модель выглядит так:

  • Обработчик делает BEGIN TRAN.
  • Делает RECEIVE из очереди.
  • Выполняет бизнес-логику.
  • Делает COMMIT.
  • Если на шаге 3 произошла ошибка и транзакция откатилась, сообщение вернётся в очередь и будет доставлено повторно. Это правильно, но требует идемпотентной бизнес-логики и защиты от poison message.

    Почему параллельные воркеры ломают бизнес-порядок

    Как только вы включаете активацию и ставите MAX_QUEUE_READERS > 1, вы сознательно допускаете параллельную обработку сообщений из одной очереди.

    При этом:

  • Очередь — общая.
  • Диалогов — много.
  • Бизнес-сущностей (например, заказов) — тоже много.
  • Если вы не предпримете специальных мер, два воркера могут одновременно обработать два сообщения, относящиеся к одной и той же бизнес-сущности, но пришедшие из разных диалогов. Например:

  • Сообщение "Order 42: Confirm".
  • Сообщение "Order 42: Cancel".
  • Если эти сообщения окажутся в разных диалогах, SB не обязан сохранить их взаимный порядок, а параллельные воркеры легко создадут гонку.

    Отсюда появляется архитектурная задача: как заставить связанные сообщения обрабатываться строго последовательно, но при этом не терять параллелизм для независимых сущностей. Решение — conversation groups.

    Conversation group: что это и какую проблему решает

    Conversation group — это механизм Service Broker, который позволяет связать несколько диалогов в одну группу так, чтобы обработка сообщений из этой группы была сериализована.

    Интуитивно:

  • Диалог — это упорядоченный канал между двумя сервисами.
  • Группа — это «замок» (lock), который гарантирует, что в один момент времени только один обработчик работает с сообщениями данной группы.
  • Это особенно важно при:

  • Активации очереди с несколькими читателями.
  • Разделении потока сообщений на несколько диалогов по техническим причинам (разные контракты, разные сроки жизни, переоткрытие диалогов).
  • Необходимости обрабатывать события по одному бизнес-ключу строго последовательно.
  • Обзорное описание механизма: Conversation Groups

    Как Service Broker обеспечивает сериализацию

    У каждой записи сообщения в очереди есть conversation_group_id. Когда обработчик делает RECEIVE, Service Broker берёт блокировку, которая предотвращает конкурентную обработку сообщений той же группы другим RECEIVE в другом сеансе.

    Из этого следует важный практический вывод:

  • Вы можете безопасно ставить MAX_QUEUE_READERS больше 1.
  • Но при этом добиваться последовательности для определённых классов сообщений, если они относятся к одной группе.
  • Практика: как создавать связанные диалоги в одной группе

    Есть два популярных подхода.

    Подход A: один диалог на бизнес-ключ

    Идея: для каждой сущности (например, OrderId) вы создаёте один долгоживущий диалог и отправляете все сообщения по нему.

    Плюсы:

  • Порядок сообщений гарантирован внутри диалога автоматически.
  • Минусы:

  • Нужно хранить conversation_handle и переоткрывать диалог при ошибках/завершении.
  • Долгоживущие диалоги требуют дисциплины закрытия и мониторинга.
  • Подход B: несколько диалогов, но одна conversation group

    Идея: диалоги могут быть короткими и независимыми, но вы связываете их в одну группу, чтобы обеспечить последовательную обработку.

    Для связывания используют опции RELATED_CONVERSATION или RELATED_CONVERSATION_GROUP в BEGIN DIALOG CONVERSATION.

    Документация: BEGIN DIALOG CONVERSATION

    Пример: начинаем первый диалог и запоминаем его conversation_group_id, затем создаём второй диалог, связанный с этой группой.

    Что происходит:

  • Вы можете использовать разные диалоги (разные conversation_handle).
  • Но сообщения из них будут иметь один conversation_group_id.
  • Следовательно, обработка на целевой очереди будет сериализована по группе.
  • Системные представления для наблюдения:

  • sys.conversation_endpoints
  • sys.conversation_groups
  • Паттерн обработчика: безопасная обработка по conversation group

    Если вы просто делаете RECEIVE TOP (1) FROM dbo.TargetQueue, Service Broker всё равно применяет групповые блокировки. Но в продвинутых сценариях удобно явно захватывать группу, а затем выбирать сообщения именно из неё.

    Для этого используют команду GET CONVERSATION GROUP.

    Документация: GET CONVERSATION GROUP

    Ниже — каркас обработчика, который:

  • Ждёт группу.
  • Берёт группу.
  • В рамках транзакции выбирает и обрабатывает несколько сообщений из этой группы.
  • Коммитит.
  • Ключевая идея этого паттерна:

  • Параллельные воркеры могут обрабатывать разные conversation_group_id одновременно.
  • Но один и тот же conversation_group_id будет обрабатываться строго одним воркером в конкретный момент времени.
  • Как проектировать группы под бизнес-порядок

    Типичный способ привязки к бизнес-ключу

    Service Broker не знает, что такое OrderId, поэтому вам нужно самим решить, как сообщения попадут в нужную группу.

    Распространённая схема:

  • В базе хранится таблица сопоставления BusinessKey -> conversation_group_id.
  • При отправке нового сообщения по бизнес-ключу приложение:
  • 1. Находит conversation_group_id по ключу. 2. Если нет — создаёт новый диалог, берёт его conversation_group_id и сохраняет. 3. Открывает новый диалог с RELATED_CONVERSATION_GROUP = ... или использует существующий диалог.
  • Все сообщения по этому ключу попадают в одну группу.
  • Важное ограничение

    Conversation group гарантирует сериализацию внутри группы, но не гарантирует бизнес-порядок сам по себе, если вы отправляете связанные сообщения в разные группы. Поэтому стратегия группировки должна быть частью протокола.

    Порядок, группы и завершение диалогов

    Conversation group не отменяет обязанность корректно завершать диалоги.

    Практические правила:

  • Если это одноразовая задача, завершайте диалог сразу после обработки полезного сообщения (END CONVERSATION).
  • Всегда обрабатывайте системные сообщения:
  • 1. http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog 2. http://schemas.microsoft.com/SQL/ServiceBroker/Error
  • Если вы храните сопоставление BusinessKey -> conversation_group_id, продумайте, когда и как очищать это сопоставление, чтобы не держать «мертвые» ключи бесконечно.
  • Документация: END CONVERSATION

    Диагностика: как понять, что порядок нарушается или группа блокируется

    Проверка, какие диалоги в какой группе

    Документация: sys.conversation_endpoints

    Проверка самих групп

    Документация: sys.conversation_groups

    Типовая причина «залипания» группы

    Группа может оказаться «заблокированной» логически, если:

  • Обработчик постоянно падает на одном и том же сообщении.
  • Транзакция откатывается.
  • Сообщение возвращается в очередь.
  • Группа снова берётся, и цикл повторяется.
  • Это классический poison message эффект, но уже на уровне группы: вы фактически блокируете всю цепочку событий по одному бизнес-ключу.

    Практическая рекомендация:

  • Делайте обработчики идемпотентными.
  • Добавляйте обработку ошибок через TRY...CATCH.
  • В тяжёлых случаях выносите проблемные сообщения в «карантин» (например, отдельную таблицу/очередь) и завершайте диалог, чтобы разблокировать группу.
  • Итоги

    В этом модуле вы зафиксировали три главные идеи, которые определяют корректность и масштабирование решений на Service Broker:

  • Порядок сообщений гарантирован только внутри одного диалога.
  • Параллелизм воркеров легко приводит к гонкам, если связанные сообщения идут в разных диалогах или обрабатываются без дисциплины.
  • Conversation groups дают управляемую сериализацию: независимые группы обрабатываются параллельно, а связанные сообщения внутри группы — последовательно.
  • В следующих материалах курса обычно логично углубляться в устойчивую обработку: шаблоны воркеров, обработку ошибок и диагностику доставки (включая ситуации, когда сообщения застревают в системных очередях и endpoint-состояниях).

    4. Маршрутизация и транспорт: endpoints, routes, remote service binding

    Маршрутизация и транспорт: endpoints, routes, remote service binding

    В предыдущих статьях курса мы работали с локальным Service Broker (SB): все сервисы и очереди жили в одной базе, а значит сообщения доставлялись без сетевого транспорта и сложной настройки безопасности.

    Как только вы переходите к обмену между разными базами на разных экземплярах SQL Server (а иногда и на разных серверах), появляются три новые опоры архитектуры:

  • Transport: сетевой канал SB между экземплярами SQL Server.
  • Routing: правила, по которым локальный экземпляр понимает, куда отправлять сообщения для удалённого сервиса.
  • Dialog security: как стороны диалога удостоверяют друг друга и (обычно) шифруют трафик.
  • Эта статья объясняет три ключевых объекта распределённого Service Broker:

  • Service Broker endpoint (транспорт)
  • Route (маршрутизация)
  • Remote service binding (привязка безопасности диалога)
  • !Общая картина: как SEND превращается в сетевую доставку через route и endpoint, а remote service binding участвует в безопасности диалога

    Карта понятий: что за что отвечает

    Endpoint

    Service Broker endpoint — это сетевой слушатель на уровне экземпляра SQL Server, который принимает и отправляет SB-трафик по TCP.

    Факты, которые важно помнить:

  • Endpoint создаётся на уровне экземпляра, а не базы.
  • Endpoint нужен именно для межэкземплярного обмена.
  • Endpoint имеет состояние (может быть остановлен) и привязан к порту.
  • Документация: CREATE ENDPOINT

    Route

    Route — это объект в базе данных, который говорит Service Broker: если отправляем на сервис с таким именем (и, при необходимости, на конкретный broker_instance), то доставлять нужно по такому адресу.

    Факты:

  • Route живёт в базе, откуда вы инициируете отправку (обычно база инициатора, но иногда и база таргета для ответов в сложных схемах).
  • В route вы указываете SERVICE_NAME, а также ADDRESS вида TCP://host:port.
  • Для более точной маршрутизации можно указать BROKER_INSTANCE (GUID удалённого брокера).
  • Документация: CREATE ROUTE

    Remote service binding

    Remote service binding — это объект в базе данных, который связывает имя удалённого сервиса с локальным пользователем и задаёт, будет ли SB использовать анонимность.

    Зачем он нужен:

  • Он включает и фиксирует модель безопасности диалога (dialog security) для конкретного удалённого сервиса.
  • Через него Service Broker понимает, от имени какого пользователя устанавливать защищённый диалог.
  • Документация: CREATE REMOTE SERVICE BINDING

    Минимальная логика доставки между экземплярами

    Когда вы выполняете SEND ON CONVERSATION, для межсерверного сценария дальше происходит следующее:

  • Сообщение попадает во внутреннюю очередь отправки transmission queue.
  • Service Broker ищет подходящий route по имени удалённого сервиса (и опционально по broker_instance).
  • SB устанавливает соединение к endpoint удалённого экземпляра.
  • Если включена безопасность диалога, SB применяет настройки, зависящие от remote service binding.
  • При успехе сообщение доставляется в очередь таргета.
  • При проблемах сообщение остаётся в transmission queue, а вы видите причину в диагностике.
  • Настройка транспорта: Service Broker endpoint

    Создание endpoint

    Ниже пример типового endpoint для SB. Его нужно создать на каждом экземпляре, который участвует в межэкземплярном обмене.

    Практические замечания:

  • Порт 4022 часто используют по умолчанию, но это не требование.
  • Если endpoint создан, но остановлен, его можно запустить.
  • Права на endpoint

    На уровне экземпляра нужно дать право CONNECT на endpoint той учётной записи (login), под которой удалённая сторона будет подключаться.

    Документация по безопасности Service Broker: Security for Service Broker

    Маршрутизация: routes

    Базовый route на удалённый сервис

    В простейшем виде вы описываете имя удалённого сервиса и адрес его endpoint.

    Важно:

  • SERVICE_NAME должен строго совпадать с именем сервиса, которое вы используете в TO SERVICE N'...'.
  • ADDRESS должен быть сетевым адресом, достижимым с инициатора (DNS, firewall, порт).
  • Зачем указывать BROKER_INSTANCE

    BROKER_INSTANCE позволяет привязать route к конкретному экземпляру брокера. Это полезно, когда:

  • имя удалённого сервиса может встретиться в нескольких средах;
  • произошёл перенос/восстановление базы на другой экземпляр и поменялся идентификатор брокера;
  • вы хотите исключить доставку “не туда”, если DNS/маршрутизация изменились.
  • Получить broker_instance (GUID брокера) можно из базы, где живёт удалённый сервис:

    После этого route можно сделать более строгим:

    BROKER_INSTANCE здесь — это значение service_broker_guid удалённой базы.

    Route для локальной доставки

    Иногда нужно явно сказать: “этот сервис локальный”. Для этого используют ADDRESS = N'LOCAL'.

    Это полезно в сценариях миграции и в сложных топологиях, где часть сервисов локальная, а часть удалённая.

    Безопасность диалога: remote service binding

    Что именно “биндится”

    Remote service binding связывает:

  • удалённый сервис (по имени);
  • локального пользователя в текущей базе;
  • и опционально указывает, можно ли использовать анонимность.
  • Пример:

    Смысл:

  • USER — это пользователь в текущей базе (не login), под которым SB будет вести защищённый диалог.
  • ANONYMOUS = OFF означает, что вы не хотите анонимный режим и ожидаете нормальную схему удостоверения.
  • Документация: CREATE REMOTE SERVICE BINDING

    Где “живёт” криптография

    Remote service binding — это только точка привязки политики. Чтобы dialog security реально работала, обычно дополнительно настраивают:

  • ключи и сертификаты в базах;
  • пользователей, сопоставленных сертификатам;
  • права на endpoint.
  • Полная схема безопасности для межсерверного обмена объёмная и зависит от требований, но важный практический вывод такой:

  • Если вы не планируете безопасность заранее, вы почти неизбежно получите сообщения, “застрявшие” в transmission queue из-за проблем аутентификации/шифрования.
  • Опорная документация: Security for Service Broker

    Как это связывается с BEGIN DIALOG и SEND

    На стороне инициатора ваша логика BEGIN DIALOG CONVERSATION почти не меняется, но в межсерверном мире появляются важные детали:

  • TO SERVICE — это имя удалённого сервиса, под которое должен существовать route.
  • По умолчанию диалог пытается использовать шифрование и безопасность (если не указано иное).
  • Пример (упрощённый):

    Если route/endpoint/security настроены корректно, сообщение уйдёт через сеть и появится в очереди таргета.

    Документация: BEGIN DIALOG CONVERSATION, SEND

    Transmission queue как главный индикатор проблем доставки

    Что это такое

    Transmission queue — внутренняя очередь, где накапливаются сообщения, ожидающие отправки на удалённую сторону.

    Проверка:

    Интерпретация:

  • Если в sys.transmission_queue есть строки и они долго не исчезают, значит доставка не завершена.
  • transmission_status обычно содержит текст причины (маршрут, сеть, безопасность, endpoint).
  • Документация: sys.transmission_queue

    Типовые причины “застревания”

  • Endpoint на удалённом экземпляре не создан или не запущен.
  • Неправильный ADDRESS в route (ошибка DNS, порт, firewall).
  • Не хватает прав CONNECT на endpoint.
  • Проблема с безопасностью диалога (сертификаты, remote service binding, доверие).
  • Диагностика маршрутизации и транспорта: что смотреть

    Метаданные routes

    Документация: sys.routes

    Состояние endpoint

    Документация: sys.endpoints

    Активные соединения Service Broker

    Документация: sys.dm_broker_connections

    Состояние диалогов

    Документация: sys.conversation_endpoints

    Практические рекомендации проектирования

  • Думайте о route как о “DNS Service Broker”: он связывает логическое имя сервиса с физическим адресом.
  • Уточняйте BROKER_INSTANCE, когда важна защита от “доставки не туда”.
  • Наблюдайте sys.transmission_queue постоянно, это главный ранний сигнал проблем транспорта.
  • Не усложняйте безопасность на первом шаге обучения, но в продакшене планируйте dialog security заранее.
  • Дисциплина завершения диалогов остаётся обязательной: удалённый транспорт не отменяет обработки EndDialog и Error.
  • Итоги

    Для межэкземплярного Service Broker вы добавляете к знакомым объектам (message types, contracts, queues, services, dialogs) ещё три критических элемента:

  • Endpoint обеспечивает сетевой транспорт между экземплярами.
  • Route определяет, по какому адресу доставлять сообщения для конкретного удалённого сервиса (и, при необходимости, для конкретного broker_instance).
  • Remote service binding задаёт привязку политики безопасности диалога к удалённому сервису.
  • Дальше по логике углублённого курса обычно переходят к устойчивой эксплуатации: мониторинг, обработка ошибок доставки, “ядовитые” сообщения, а также разбор типовых состояний в sys.conversation_endpoints и причин накопления записей в sys.transmission_queue.

    5. Активация и обработчики: internal/external activation, concurrency, idempotency

    Активация и обработчики: internal/external activation, concurrency, idempotency

    Service Broker (SB) даёт надёжную доставку сообщений и транзакционное получение через RECEIVE. Но реальная ценность появляется только тогда, когда у вас есть обработчики (воркеры), которые забирают сообщения из очереди и выполняют бизнес-логику.

    В предыдущих статьях курса мы:

  • собрали базовую конфигурацию MESSAGE TYPECONTRACTQUEUESERVICE;
  • разобрали диалоги и conversation groups как инструмент управляемой последовательности;
  • посмотрели транспорт и маршрутизацию для межсерверного обмена.
  • Теперь мы разберём эксплуатационно важную часть: как именно запускать обработчики, как проектировать параллелизм и как добиться корректности при повторах доставки.

    !Сравнение внутренней (internal) и внешней (external) активации и путь сообщения к обработчику

    Что такое активация и что такое обработчик

    Обработчик в SB обычно реализуется как хранимая процедура, которая:

  • ждёт сообщение в очереди;
  • получает его через RECEIVE;
  • выполняет бизнес-логику;
  • отправляет ответ (если протокол предполагает ответ);
  • корректно завершает диалог (END CONVERSATION), включая системные сообщения EndDialog и Error.
  • Активация отвечает на вопрос: кто и когда запускает обработчик.

    Есть два базовых подхода:

  • internal activation (внутренняя активация очереди): SQL Server сам запускает вашу процедуру, когда в очереди появляются сообщения.
  • external activation (внешняя активация): отдельный процесс (служба, агент, приложение) сам делает RECEIVE из очереди и обрабатывает сообщения.
  • Internal activation: как работает и что важно понимать

    Внутренняя активация настраивается на очереди и запускает указанную процедуру при наличии сообщений.

    Основные параметры:

  • PROCEDURE_NAME — имя процедуры-воркера.
  • MAX_QUEUE_READERS — максимум параллельных экземпляров процедуры, которые SQL Server может запускать для этой очереди.
  • EXECUTE AS — контекст безопасности выполнения.
  • Настройка выполняется через ALTER QUEUE (или задаётся сразу в CREATE QUEUE). Документация: ALTER QUEUE

    Минимальная настройка внутренней активации

    Практический смысл MAX_QUEUE_READERS:

  • 1 даёт минимальный параллелизм и проще в отладке.
  • > 1 повышает пропускную способность, но создаёт конкуренцию за ресурсы и повышает требования к корректности обработчика.
  • External activation: когда она нужна и как обычно делается

    Внешняя активация означает, что SQL Server не запускает воркеры сам. Вместо этого внешний процесс:

  • открывает соединение к базе;
  • в цикле выполняет WAITFOR (RECEIVE ...);
  • обрабатывает сообщения;
  • фиксирует транзакцию.
  • Когда это оправдано:

  • обработка включает долгие операции, нежелательные внутри SQL Server (вызовы внешних API, ожидания сети, тяжёлые вычисления);
  • нужно использовать библиотеку/SDK, которого нет в T-SQL;
  • вы хотите независимое масштабирование воркеров вне SQL Server.
  • Что важно не сломать при внешней активации:

  • получать сообщения транзакционно;
  • корректно завершать диалоги;
  • проектировать идемпотентность так же, как и при внутренней активации.
  • Конкурентность: откуда берётся параллелизм и что он ломает

    В SB параллелизм появляется из нескольких источников:

  • несколько экземпляров процедуры при internal activation (MAX_QUEUE_READERS > 1);
  • несколько внешних воркеров при external activation;
  • пакетная обработка (RECEIVE TOP (N)), когда вы ускоряете обработку объёмом.
  • Параллелизм может ломать:

  • бизнес-порядок (если связанные сообщения обрабатываются параллельно);
  • целостность данных (если обработчик не защищён от гонок);
  • производительность (если воркеры начинают ждать блокировки друг друга).
  • Из предыдущей статьи ключевая идея такая:

  • порядок гарантирован внутри одного диалога;
  • для последовательности по бизнес-ключу при параллельных воркерах используют conversation groups.
  • Документация: Conversation Groups

    Базовый шаблон обработчика для internal activation

    Ниже каркас, который покрывает обязательные технические элементы:

  • WAITFOR (RECEIVE ...) TIMEOUT чтобы корректно выйти, если очередь пустая;
  • транзакция вокруг RECEIVE и бизнес-логики;
  • обработка системных сообщений EndDialog и Error;
  • защита от падения через TRY...CATCH.
  • Почему цикл внутри процедуры важен:

  • SQL Server может запускать процедуру активации “в ответ на событие”, но вам выгоднее обработать несколько сообщений за один запуск, чем стартовать/останавливать воркер на каждое сообщение.
  • Poison message: почему очередь может сама отключиться

    Если обработчик постоянно получает одно и то же сообщение и постоянно откатывает транзакцию, сообщение возвращается в очередь и ситуация становится бесконечной. Это называется poison message.

    Особенность internal activation:

  • SQL Server имеет встроенную защиту от poison message и может отключить очередь, чтобы остановить бесконечный цикл ошибок.
  • Документация: Poison message handling

    Практическая стратегия:

  • оборачивать обработку в TRY...CATCH;
  • логировать проблему;
  • при невозможности обработать сообщение переносить его в “карантин” (например, таблица ошибок) и завершать диалог, чтобы не блокировать поток;
  • делать бизнес-операции идемпотентными (следующий раздел).
  • Идемпотентность: как переживать повторы доставки

    Идемпотентность обработчика означает: если одно и то же сообщение будет обработано повторно, итоговое состояние данных останется корректным.

    Почему повторы возможны даже при “надёжной очереди”:

  • обработчик упал после RECEIVE, но до COMMIT;
  • транзакция была откатана из-за дедлока/таймаута;
  • при внешней активации воркер был убит во время транзакции;
  • часть протокола была выполнена, а подтверждение не дошло.
  • Практический паттерн: таблица дедупликации по техническому ключу

    В представлении очереди доступны conversation_handle и message_sequence_number. Внутри одного диалога message_sequence_number растёт, что позволяет различать сообщения.

    Идея:

  • при обработке фиксировать факт “это сообщение уже применено” в отдельной таблице;
  • сделать уникальный ключ, чтобы повторная попытка не применяла изменения второй раз.
  • Пример таблицы:

    Пример обработки с дедупликацией внутри транзакции:

    Ключевое требование:

  • запись в таблицу дедупликации и бизнес-изменения должны быть в одной транзакции, иначе при сбое между ними вы не получите гарантии.
  • Паттерн: идемпотентность по бизнес-ключу

    Иногда правильнее защищаться не по техническому идентификатору сообщения, а по бизнес-смыслу:

  • у сообщения есть OrderId;
  • вы в таблице состояния храните “последнюю применённую версию/статус”;
  • повтор события приводит к тому, что UPDATE ничего не меняет.
  • Подходы:

  • UPDATE ... WHERE ... AND Status <> @newStatus;
  • уникальные ключи на таблицах фактов (например, уникальный ExternalEventId);
  • MERGE или INSERT ... WHERE NOT EXISTS с правильными блокировками.
  • Параллелизм без гонок: связываем concurrency и conversation groups

    Если у вас MAX_QUEUE_READERS > 1, два воркера могут одновременно читать одну очередь. Чтобы связанные сообщения обрабатывались последовательно:

  • помещайте связанные диалоги в одну conversation group;
  • обрабатывайте работу “по группе”, а не “случайное TOP(1)”.
  • Технический инструмент на стороне воркера: GET CONVERSATION GROUP.

    Документация: GET CONVERSATION GROUP

    Каркас воркера “по группе”:

    Что это даёт:

  • разные группы обрабатываются параллельно разными воркерами;
  • внутри одной группы сохраняется сериализация, и вы избегаете гонок по одному бизнес-ключу.
  • Производительность обработчиков: практические настройки

    Пакетная обработка

    RECEIVE TOP (N) снижает накладные расходы на транзакции и переключения контекста.

    Риски:

  • слишком большой N увеличивает время удержания блокировок и может ухудшить задержку для других групп.
  • Типичная практика:

  • начинать с небольших пакетов (например, 10–100) и измерять.
  • Размер транзакции

    Чем длиннее транзакция внутри воркера, тем выше риск:

  • дедлоков;
  • блокировок по данным бизнес-таблиц;
  • эффекта “один медленный воркер тормозит всё”.
  • Техническое правило:

  • внутри транзакции держите только то, что нужно для корректности: RECEIVE + фиксация/применение изменения + подтверждающие действия.
  • Настройка MAX_QUEUE_READERS

  • MAX_QUEUE_READERS масштабирует воркеры, но не гарантирует рост производительности “линейно”.
  • если вы используете conversation groups, параллелизм ограничивается числом активных групп.
  • Наблюдаемость: как понять, что активация работает

    Состояние очереди и активации

    Документация: sys.service_queues

    Мониторы очередей

    sys.dm_broker_queue_monitors помогает понять, активна ли очередь и видит ли SQL Server нагрузку.

    Документация: sys.dm_broker_queue_monitors

    Если сообщения “не уходят” при межсерверной доставке

    Для распределённых сценариев ключевая точка — sys.transmission_queue.

    Документация: sys.transmission_queue

    Итоги

  • Internal activation удобна тем, что SQL Server сам запускает воркеры, но требует аккуратной обработки ошибок и понимания poison message.
  • External activation полезна для долгих и внешних операций, но ответственность за жизненный цикл воркеров и масштабирование лежит на вас.
  • Concurrency контролируется через MAX_QUEUE_READERS, пакетирование и (в важных случаях) через conversation groups.
  • Idempotency обязательна: повторы доставки — нормальный рабочий режим при сбоях, и обработчик должен быть устойчивым к повторной обработке.
  • В следующем углублении курса обычно логично переходить к диагностике и эксплуатации: типовые состояния sys.conversation_endpoints, причины накопления в sys.transmission_queue, стратегии ретраев и “карантина” сообщений, а также к шаблонам безопасного завершения диалогов в сложных протоколах.

    6. Надёжность и безопасность: транзакции, poison message, шифрование, сертификаты

    Надёжность и безопасность: транзакции, poison message, шифрование, сертификаты

    Service Broker (SB) часто выбирают не за удобство API, а за две фундаментальные вещи:

  • Надёжность: сообщения не теряются при сбоях и обрабатываются транзакционно.
  • Безопасность: обмен между экземплярами SQL Server можно шифровать и аутентифицировать.
  • В прошлых модулях мы собрали базовые объекты, разобрали диалоги, порядок доставки, conversation groups, маршрутизацию (endpoints, routes, remote service binding) и активацию. Теперь свяжем это в эксплуатационно зрелую картину: как обеспечивается надёжность, почему появляются poison message, и как правильно подойти к шифрованию и сертификатам.

    !Два уровня безопасности: транспортный и диалоговый

    Надёжность Service Broker через транзакции

    Что именно гарантирует SB

    В рамках одного диалога SB обеспечивает свойства, которые обычно формулируют как exactly-once-in-order delivery для приложений, которые корректно используют транзакции.

    Практическая интерпретация:

  • Порядок гарантирован внутри одного диалога (мы закрепили это в модуле про диалоги и conversation groups).
  • Получение транзакционно: RECEIVE участвует в транзакции SQL Server.
  • Отправка транзакционно: SEND может быть частью той же транзакции, что и ваши DML-операции.
  • Официальные команды:

  • RECEIVE (Transact-SQL)
  • SEND (Transact-SQL)
  • Базовый принцип: бизнес-изменения и RECEIVE должны жить в одной транзакции

    Если вы хотите, чтобы сообщение считалось обработанным только тогда, когда бизнес-операция зафиксирована, то типовой шаблон такой:

    Ключевая идея: если обработчик упал до COMMIT, сообщение вернётся в очередь и будет доставлено снова.

    Почему идемпотентность всё равно обязательна

    Даже при транзакционном RECEIVE повторы обработки возможны и нормальны:

  • дедлок и откат транзакции;
  • таймаут блокировки;
  • аварийное завершение сессии;
  • внешние побочные эффекты, которые нельзя включить в транзакцию SQL Server (HTTP-вызовы, запись в стороннюю систему).
  • Поэтому рекомендации из модуля про обработчики остаются обязательными: делайте обработку идемпотентной и фиксируйте факт обработки (например, по (conversation_handle, message_sequence_number)), если это соответствует протоколу.

    Poison message: почему очередь может выключиться сама

    Что такое poison message в терминах SB

    Poison message в SB проявляется так:

  • сообщение получено обработчиком;
  • обработчик падает или явно делает ROLLBACK;
  • сообщение возвращается в очередь;
  • активация запускает обработчик снова;
  • цикл повторяется.
  • Если это происходит в режиме internal activation, SQL Server включает защиту: после нескольких подряд откатов на одном сообщении очередь может быть отключена, чтобы остановить бесконечную нагрузку.

    Документация:

  • Poison message handling
  • Как понять, что очередь отключилась из-за poison message

    Проверьте состояние очереди:

    Документация:

  • sys.service_queues (Transact-SQL)
  • Дополнительно полезно смотреть монитор очереди:

    Документация:

  • sys.dm_broker_queue_monitors (Transact-SQL)
  • Тактика обработки poison message

    Цель не в том, чтобы отключить защиту, а в том, чтобы:

  • не зацикливаться;
  • не блокировать бизнес-поток;
  • сохранить данные для расследования.
  • Обычно используют комбинацию:

  • TRY...CATCH вокруг обработки;
  • логирование ошибок в таблицу;
  • «карантин» для проблемных сообщений;
  • корректное завершение диалога, чтобы освободить ресурсы.
  • Пример «карантинной» таблицы (идея, не стандарт SB):

    И фрагмент обработчика, который при фатальной ошибке «паркует» сообщение и завершает диалог:

    Когда уместно отключать встроенную обработку poison message

    Иногда (обычно временно, на период диагностики) отключают автоматическую защиту.

    Команда:

    Документация:

  • ALTER QUEUE (Transact-SQL)
  • Практическое правило:

  • отключение защиты не исправляет проблему, оно лишь позволяет очереди продолжать работу при риске бесконечных повторов.
  • Безопасность в Service Broker: два уровня

    В распределённых сценариях (между экземплярами) важно различать два независимых уровня.

    | Уровень | Что защищает | Чем настраивается | Типовые объекты | |---|---|---|---| | Transport security | соединение экземпляр-экземпляр | конфигурация endpoint, его аутентификация и права | CREATE ENDPOINT, GRANT CONNECT ON ENDPOINT | | Dialog security | диалог сервис-сервис | сертификаты, пользователи из сертификатов, remote service binding | CREATE CERTIFICATE, CREATE USER, CREATE REMOTE SERVICE BINDING |

    Обзорная документация:

  • Security for Service Broker
  • Шифрование диалогов: что означает WITH ENCRYPTION

    В BEGIN DIALOG CONVERSATION можно управлять требованием шифрования:

  • WITH ENCRYPTION = ON означает, что диалог должен быть защищён.
  • WITH ENCRYPTION = OFF часто используют только в локальных демо.
  • Документация:

  • BEGIN DIALOG CONVERSATION (Transact-SQL)
  • Практический смысл:

  • шифрование в SB тесно связано с диалоговой безопасностью, а диалоговая безопасность в реальных системах почти всегда завязана на сертификаты;
  • при неверной настройке безопасности сообщения обычно «застревают» в sys.transmission_queue с диагностическим текстом.
  • Сертификаты: практическая модель для распределённого SB

    Ниже не «универсальный единственный способ», а рабочая опорная схема, которая помогает понять механику.

    Сценарий:

  • SQL1 хостит базу InitiatorDB и сервис //demo/InitiatorService.
  • SQL2 хостит базу TargetDB и сервис //demo/TargetService.
  • Экземпляры общаются через Service Broker endpoint.
  • Шаг 1: подготовить ключи и сертификаты в каждой базе

    В каждой базе создают master key (если нет) и сертификат.

    На InitiatorDB:

    На TargetDB:

    Документация:

  • CREATE MASTER KEY (Transact-SQL)
  • CREATE CERTIFICATE (Transact-SQL)
  • Шаг 2: обменяться публичными сертификатами

    Обычно сертификаты экспортируют в файл на каждом сервере и импортируют на другой стороне.

    Экспорт на SQL1:

    Импорт на SQL2 в TargetDB:

    Экспорт на SQL2:

    Импорт на SQL1 в InitiatorDB:

    Документация:

  • BACKUP CERTIFICATE (Transact-SQL)
  • Шаг 3: создать пользователей из сертификатов и дать им минимальные права

    Идея: удалённая сторона будет представлена в вашей базе пользователем, созданным из её сертификата.

    На InitiatorDB:

    На TargetDB:

    Практическое замечание:

  • точный набор прав зависит от того, кто у вас инициирует диалоги, кто принимает, и какой способ аутентификации выбран;
  • ключевой объект, который «включает» диалоговую безопасность по имени удалённого сервиса, это remote service binding.
  • Шаг 4: создать remote service binding

    Remote service binding связывает имя удалённого сервиса и локального пользователя, от имени которого будет устанавливаться защищённый диалог.

    На InitiatorDB:

    На TargetDB (если таргет будет инициировать сообщения в ответных/обратных сценариях, либо для симметричной конфигурации):

    Документация:

  • CREATE REMOTE SERVICE BINDING (Transact-SQL)
  • Важно:

  • remote service binding сам по себе не «создаёт шифрование», он задаёт политику и контекст безопасности для диалога с указанным сервисом;
  • при ошибках конфигурации вы почти всегда увидите причину в sys.transmission_queue.
  • Endpoint и права: минимальная проверка транспорта

    Transport layer настраивается endpoint-ами на уровне экземпляра.

    Создание endpoint (типовой пример):

    Документация:

  • CREATE ENDPOINT (Transact-SQL)
  • Проверка состояния endpoint:

    Документация:

  • sys.endpoints (Transact-SQL)
  • Практическая проверка соединений SB:

    Документация:

  • sys.dm_broker_connections (Transact-SQL)
  • Диагностика безопасности: где искать первопричину

    Transmission queue как главный индикатор

    Если сообщение не доставляется на удалённую сторону, оно будет висеть в transmission queue, а в transmission_status обычно будет текстовая причина.

    Документация:

  • sys.transmission_queue (Transact-SQL)
  • Типовые причины, которые почти всегда сводятся к безопасности/маршрутизации:

  • неправильный ROUTE или недоступный ADDRESS;
  • endpoint не запущен или порт закрыт firewall;
  • нет CONNECT на endpoint;
  • неверная настройка dialog security (сертификаты, пользователи, remote service binding).
  • Состояние диалогов

    Документация:

  • sys.conversation_endpoints (Transact-SQL)
  • Практические правила надёжности и безопасности

  • Всегда обрабатывайте EndDialog и Error и завершайте диалоги, иначе будет утечка состояния.
  • Держите RECEIVE и бизнес-изменения в одной транзакции, иначе вы получите повторы с неконсистентностью.
  • Включайте идемпотентность как нормальный режим работы при сбоях и дедлоках.
  • Для internal activation проектируйте обработчик так, чтобы не возникал poison loop: TRY...CATCH, карантин, завершение диалога при фатальной ошибке.
  • В распределённых сценариях наблюдайте sys.transmission_queue: это самый быстрый путь к причине проблемы.
  • Различайте transport security и dialog security: endpoint решает одно, сертификаты и remote service binding решают другое.
  • Итоги

    В зрелом решении на Service Broker надёжность и безопасность строятся на нескольких «несдвигаемых» опорах:

  • Транзакционность SEND и RECEIVE даёт надёжную доставку и корректную обработку при сбоях.
  • Poison message возникает не из-за SB, а из-за логики обработчика, которая бесконечно откатывает одно и то же сообщение.
  • В распределённом SB безопасность двухуровневая: endpoint обеспечивает транспорт, а сертификаты и remote service binding обеспечивают безопасность диалога.
  • sys.transmission_queue и sys.conversation_endpoints являются основными точками диагностики проблем доставки и безопасности.
  • Следующий логичный шаг углубления после этого модуля: систематическая диагностика и эксплуатация в продакшене, включая разбор типовых state_desc у conversation endpoints, массовое завершение «сломанных» диалогов и безопасные процедуры восстановления доставки.

    7. Эксплуатация: мониторинг, диагностика, производительность и шаблоны решений

    Эксплуатация: мониторинг, диагностика, производительность и шаблоны решений

    Service Broker (SB) часто начинают использовать как встроенную очередь, но в эксплуатации он быстро проявляет себя как система с состоянием: диалоги живут, сообщения могут временно «застревать» в системных очередях, обработчики конкурируют за ресурсы, а неверная обработка ошибок приводит к poison message и отключению очередей.

    В предыдущих статьях курса мы разобрали:

  • архитектуру и ключевые понятия;
  • конфигурацию объектов (типы сообщений, контракты, очереди, сервисы);
  • диалоги, порядок доставки и conversation groups;
  • транспорт и маршрутизацию (endpoints, routes, remote service binding);
  • активацию, конкурентность и идемпотентность;
  • надёжность и безопасность, включая poison message и сертификаты.
  • Эта статья — про эксплуатацию: что мониторить, как диагностировать проблемы доставки и обработки, как подходить к производительности и какие шаблоны решений помогают не изобретать систему заново.

    !Схема помогает запомнить, где “живёт” проблема: в очереди, в диалоге, в передаче или в обработчике

    Наблюдаемость Service Broker как система сигналов

    Эксплуатационно полезно мыслить не «таблицами очередей», а сигналами из нескольких слоёв:

  • слой очереди (включена ли очередь, включён ли receive, работает ли активация);
  • слой обработчиков (сколько активных задач запущено, не зациклились ли);
  • слой диалогов (в каком состоянии endpoints, не копится ли “мёртвое” состояние);
  • слой доставки (есть ли backlog в sys.transmission_queue, что в transmission_status);
  • слой транспорта (есть ли broker-соединения, живы ли endpoints, есть ли сетевые ошибки).
  • Минимальный набор представлений и DMV, который стоит знать

    | Что проверяем | Где смотреть | Зачем это нужно | |---|---|---| | Очереди и активация | sys.service_queues | Понять, включена ли очередь, включена ли активация, сколько читателей | | Состояние активации очередей | sys.dm_broker_queue_monitors | Увидеть, активируется ли очередь и когда | | Активные задачи активации | sys.dm_broker_activated_tasks | Понять, запущены ли воркеры и сколько | | Состояние диалогов | sys.conversation_endpoints | Диагностика зависших/сломанных диалогов | | Backlog на отправке | sys.transmission_queue | Главный индикатор проблем маршрутизации/сети/безопасности | | Broker-соединения | sys.dm_broker_connections | Подтвердить наличие транспортного соединения между экземплярами | | Состояние endpoints | sys.endpoints | Проверить, что SERVICE_BROKER endpoint запущен |

    Мониторинг очередей и активации

    Проверка: очередь включена и способна принимать RECEIVE

    Практическая интерпретация:

  • is_receive_enabled = 0 означает, что очередь фактически не будет обслуживаться (часто это последствие poison message или ручного отключения).
  • is_activation_enabled = 1 означает, что SQL Server может запускать процедуру активации при наличии сообщений.
  • max_readers помогает понять целевой параллелизм.
  • Проверка: монитор очереди видит работу

    Что полезно вынести:

  • если state_desc долго не меняется и last_activated_time пустой/старый, активация могла не запускаться;
  • если очередь не пуста, а активация не стартует, это почти всегда либо конфигурация, либо блокировки, либо постоянные ошибки воркера.
  • Проверка: реально ли запущены воркеры активации

    Зачем это нужно:

  • отличить ситуацию «сообщения не обрабатываются, потому что воркер не запускается» от «воркер запущен, но стоит/падает/ждёт блокировки».
  • Диагностика доставки: когда сообщения “застревают”

    Для распределённого SB (между экземплярами) первое, что проверяют — sys.transmission_queue.

    > The transmission queue is a system table that contains messages that are waiting to be sent to the remote service. — Microsoft Learn, sys.transmission_queue

    Быстрая проверка backlog на отправке

    Как читать результат:

  • наличие строк означает, что сообщения ещё не подтверждены как доставленные на удалённую сторону;
  • transmission_status обычно даёт текст причины (маршрут, endpoint, сеть, безопасность).
  • Частые причины backlog в transmission queue

  • неверный route (не совпало SERVICE_NAME, неверный ADDRESS);
  • удалённый endpoint не запущен или порт закрыт firewall;
  • нет CONNECT на endpoint;
  • проблемы dialog security (сертификаты, remote service binding, доверие);
  • “разъехался” service_broker_guid после восстановления базы и не обновили BROKER_INSTANCE в route.
  • Быстрые проверки транспорта

    Проверить endpoint на экземпляре:

    Проверить broker-соединения:

    Если нет broker-соединения, а sys.transmission_queue растёт, проблема почти всегда в маршрутизации/сети/endpoint/правах.

    Диагностика диалогов: почему копится состояние и что с ним делать

    Диалог — это состояние в sys.conversation_endpoints. Если его не завершать (END CONVERSATION) и не обрабатывать системные сообщения EndDialog/Error, endpoints будут копиться.

    Поиск “подозрительных” диалогов

    Как использовать это в эксплуатации:

  • если endpoints в “нефинальных” состояниях копятся и не уменьшаются, чаще всего приложение/воркер не закрывает диалоги;
  • если много диалогов в состояниях, связанных с ошибкой, это может быть проблема контракта, маршрутизации, безопасности, либо массовая ошибка в обработчиках.
  • Документация: sys.conversation_endpoints

    Принцип “всегда закрываем системные сообщения”

    Минимальное правило обработчика:

  • на http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog делаем END CONVERSATION;
  • на http://schemas.microsoft.com/SQL/ServiceBroker/Error делаем END CONVERSATION (и логируем причину).
  • Иначе вы получите:

  • рост sys.conversation_endpoints;
  • рост внутреннего состояния SB;
  • усложнение диагностики (шум из “старых” диалогов).
  • Типовые эксплуатационные инциденты и диагностика “сверху вниз”

    Сообщения копятся в пользовательской очереди и не обрабатываются

    Диагностический маршрут:

  • Проверить, включена ли очередь (sys.service_queues.is_receive_enabled).
  • Проверить, включена ли активация и корректна ли процедура (is_activation_enabled, activation_procedure).
  • Проверить, запускаются ли воркеры (sys.dm_broker_activated_tasks).
  • Проверить ошибки воркера (логирование приложения, таблица ошибок, Extended Events).
  • Проверить блокировки бизнес-таблиц (если воркер жив, но “стоит”).
  • Очередь отключилась (часто после poison message)

    Симптом:

  • is_receive_enabled = 0 у очереди.
  • Дальнейшие шаги:

  • найти и исправить сообщение/ветку обработки, которая приводит к постоянному откату;
  • если используете internal activation, убедиться, что обработчик не падает бесконечно на одном сообщении;
  • использовать карантин: сохранять проблемное сообщение и завершать диалог, чтобы разблокировать поток.
  • Справка по механизму: Poison message handling

    Сообщения “не уходят” на удалённый сервер

    Главные индикаторы:

  • строки в sys.transmission_queue;
  • информативный transmission_status.
  • Дальше:

  • проверить route (sys.routes);
  • проверить endpoint и доступность порта;
  • проверить sys.dm_broker_connections;
  • проверить диалоговую безопасность (сертификаты, remote service binding) и права.
  • Документация: sys.routes

    Производительность: как не “убить” SQL Server Service Broker-воркерами

    Производительность SB почти всегда ограничивается не “скоростью очереди”, а тем, как написаны обработчики и как они взаимодействуют с бизнес-таблицами.

    Главные регуляторы производительности

  • Размер транзакции: чем длиннее транзакция у воркера, тем выше риск блокировок и дедлоков.
  • Пакетирование: RECEIVE TOP (N) уменьшает накладные расходы, но увеличивает время удержания блокировок.
  • Параллелизм: MAX_QUEUE_READERS (internal) или количество процессов (external) задаёт конкуренцию.
  • Conversation groups: дают параллелизм между группами и последовательность внутри группы.
  • Практический шаблон “короткая транзакция, длинная работа вне транзакции”

    Если работа тяжёлая (например, расчёты или подготовка данных), часто выгоднее разделить:

  • Внутри короткой транзакции: RECEIVE + запись минимального состояния что делать в таблицу задач.
  • За пределами транзакции SB: выполнение тяжёлой операции по данным из таблицы задач.
  • Это снижает:

  • удержание блокировок в очереди и бизнес-таблицах;
  • вероятность poison-loop из-за длинных операций.
  • Цена подхода:

  • нужно обеспечить идемпотентность “второго шага” (по ключу задачи), потому что возможны повторы.
  • Как выбирать MAX_QUEUE_READERS

    Практическая стратегия:

  • начинать с 1–2 для стабильности и прогнозируемости;
  • увеличивать после измерений:
  • - загрузка CPU; - время обработки одного сообщения; - конкуренция за одни и те же бизнес-таблицы; - число активных conversation groups.

    Если вы используете conversation groups по бизнес-ключу, то верхняя полезная граница параллелизма часто близка к числу одновременно активных групп, а не к числу сообщений.

    RECEIVE TOP (N): когда пакетирование помогает, а когда мешает

    Пакетирование помогает, когда:

  • сообщения мелкие;
  • обработка короткая;
  • важна пропускная способность.
  • Пакетирование мешает, когда:

  • обработка затрагивает “горячие” строки в одних и тех же таблицах;
  • важна минимальная задержка для разных бизнес-ключей;
  • вы используете GET CONVERSATION GROUP и долго держите группу в одной транзакции.
  • Шаблоны решений на Service Broker

    Ниже — практические шаблоны, которые часто повторяются в продакшене.

    Fire-and-forget (односторонняя команда)

    Описание:

  • инициатор отправляет команду;
  • таргет выполняет и закрывает диалог;
  • инициатор получает EndDialog и закрывает у себя.
  • Ключевые требования:

  • обработка EndDialog/Error на обеих сторонах;
  • идемпотентность на таргете (повторы нормальны).
  • Request-reply (запрос-ответ)

    Описание:

  • инициатор отправляет запрос;
  • таргет отправляет ответ и закрывает;
  • инициатор получает ответ, затем закрывает.
  • Ключевые требования:

  • контракт строго фиксирует направления сообщений;
  • тайм-ауты и обработка ошибок (инициатор должен уметь завершить диалог при отказе или просрочке);
  • не держать транзакцию открытой на время ожидания ответа.
  • Последовательность по бизнес-ключу через conversation groups

    Описание:

  • все сообщения по одному ключу попадают в одну группу;
  • воркер берёт группу через GET CONVERSATION GROUP и обрабатывает последовательно.
  • Ключевые требования:

  • стратегия формирования групп (сопоставление BusinessKey -> conversation_group_id);
  • карантин и обработка ошибок, чтобы “ядовитое” сообщение не блокировало весь ключ.
  • Inbox/Outbox как дисциплина идемпотентности

    Смысл:

  • Inbox (приём): фиксировать факт обработки сообщения (например, (conversation_handle, message_sequence_number)), чтобы повтор не применял изменения дважды.
  • Outbox (отправка): формировать сообщения на отправку из таблицы в той же транзакции, что и бизнес-изменение, чтобы не потерять событие.
  • В SB это часто выглядит как:

  • бизнес-транзакция пишет запись “к отправке”;
  • отдельный воркер превращает записи в SEND;
  • получатель ведёт дедупликацию.
  • Этот шаблон особенно полезен, когда есть побочные эффекты вне SQL Server.

    Рекомендации по эксплуатации “по умолчанию”

  • Всегда иметь регламент проверки:
  • - sys.service_queues; - sys.dm_broker_queue_monitors; - sys.dm_broker_activated_tasks; - sys.transmission_queue; - sys.conversation_endpoints.
  • Всегда логировать ошибки обработчиков в таблицу или централизованно.
  • Никогда не игнорировать EndDialog и Error.
  • Держать транзакции воркеров короткими.
  • Планировать идемпотентность как обязательную часть протокола.
  • Итоги

    Эксплуатация Service Broker — это управление очередями, обработчиками, диалогами и транспортом как единой системы:

  • Мониторинг начинается с очередей и активации, продолжается transmission queue и состояниями endpoints.
  • Диагностика обычно быстрее всего идёт через sys.transmission_queue.transmission_status и sys.conversation_endpoints.state_desc.
  • Производительность определяется транзакциями, пакетированием, параллелизмом и тем, как вы ограничиваете гонки (conversation groups).
  • Шаблоны решений (fire-and-forget, request-reply, последовательность по ключу, inbox/outbox) позволяют проектировать предсказуемые и сопровождаемые системы.
  • Следующий логичный шаг углубления курса после этой статьи — собрать “операционную практику”: готовые скрипты для ежедневной проверки, аварийного разбора backlog, безопасного завершения сломанных диалогов, и подходы к нагрузочному тестированию обработчиков под реальными профилями сообщений.