Краткое введение в сервис-брокер

В 1997 г. в составе NT4 Option Pack был выпущен MSMQ 1.0 - см. https://www.emanual.ru/download/www.eManual.ru_4364.html.

Восемь лет спустя механизм асинхронной работы на основе очередей под названием Service Broker появился в составе SQL Server 2005. Мы его уже затрагивали в посте про Event Notification. Здесь придется коснуться его чуть более подробно, т.к. далее он понадобится в развитии репликации с помощью Change Tracking на распределенный сценарий. Подобно MSMQ сервис-брокер предназначен для асинхронного взаимодействия, что позволяет реализовать отсоединенные сценарии, когда SQL Serverное приложение отправляет сообщение другому SQL Serverному приложению и может заниматься дальше своими делами, не подвисая в ожидании ответа от удаленного участника. В MSMQ, он же Queued Components в СОМ+, размер сообщения ограничен 4 МБ, в сервис-брокере сообщение представляет собой, вообще говоря, varbinary(max) - 2 ГБ. Поддерживается (в пределах беседы) порядок и корреляция сообщений, нет нужды прибегать к дополнительному программированию для поддержки транзакций, синхронизации и блокировкам при одновременной обработке очереди с нескольких потоков, резервного копирования и пр., т.к. все это является штатной функциональностью SQL Server, а очереди в сервис-брокере построены на основе таблиц. То есть данные сообщения и транзакционные бизнес-данные в случае SQL Server имеют одинаковую природу и хранятся вместе, что разом снимает массу головной боли, присущей архитектуре MSMQ, вообще всем очередникам общего назначения. Однако это накладывает свою специфику - сервис-брокер есть составная часть SQL Server и работает только с ним. Это не есть излишне строгое ограничение, поскольку SQL Server является нынче неотъемлемым атрибутом каждого уважающего себя разработчика. Кстати, все уже поставили себе 2010 СТР2 (https://technet.microsoft.com/en-us/evalcenter/ee315247.aspx)? Сравнение сервис-брокера с MSMQ можно посмотреть, например, здесь - https://www.devx.com/dbzone/Article/34110.

Берем два сервера, которые хотят иметь промеж собой сношение посредством сообщений. Мне доводилось немного рассказывать и показывать веселые картинки про сервис-брокер, поэтому первый сервер я традиционно назову Маша. Второй та часть аудитории, чье воспитание испорчено MySQL или Oracle, не менее традиционно предлагает назвать Вовочка или в том же духе. У нас он будет называться Дубровский. Это будет культурный сервер, т.е. на нем тоже будет стоять SQL Server. Маша и Дубровский будут взаимодействовать при помощи сервис-брокера. Сервис-брокер имеется в SQL Server 2005 и выше и входит во все редакции, включая бесплатный Express. Единственно, Express может общаться только со взрослой редакцией. Между собой экспрессы взаимодействовать через сервис-брокер не могут. Теорию на тему сервис-брокера можно почитать в официальной документации. Режет слух "образцы" вместо "примеры", а так ничего, читать можно. В конце концов, ведь не пробы, не выборки, не шаблоны и не сэмплы. Желающие могут обратиться к классике - «A first look at SQL Server 2005 for developers», гл. 15, либо посмотреть сравнительно недавнюю книжицу про сервис-брокер в версии 2008. Она так и называется - «Pro SQL Server 2008 Service Broker». Читать обе смысла нет, т.к. по сравнению с 2005 сервис-брокер радикальных изменений не претерпел, и автор последней сильно позаимствовал.

В моем случае по бедности роль Маши и Дубровского будет попеременно выполнять один извращенный сервер. Маша хочет послать Дубровскому сообщение <Preved/>. Как водится, выберем для экспериментов отдельную базу. На ней должен быть включен сервис-брокер. Сервис-брокер может быть включен в том числе на системных базах, кроме master и mssqlsystemresource. Включение брокера выполняется при помощи команды

 

alter database tempdb set enable_broker with rollback immediate

Скрипт 1

Эта операция требует exclusive access на базу, поэтому, возможно, понадобится указать with rollback immediate, иначе она будет выполняться бесконечно. Выключение, соответственно, делается set disable_broker. При выключении база теряет возможность отправлять и принимать сообщения, однако все ранее созданные сервисы, очереди, контракты, типы сообщений, диалоги остаются. Проверить, включен ли сервис-брокер над базой можно при помощи

select is_broker_enabled from sys.databases where name = 'tempdb'

Скрипт 2

Также сильно рекомендуется иметь на базе мастер-ключ. Иначе она, возможно, будет работать, но до поры до времени. В один прекрасный момент сообщения перестанут уходить, а будут навечно оседать в transmission queue (служебная очередь на отправителе, куда сваливаются сообщения перед отправкой). В колонке transmission_status можно узреть причину:

select cast(message_body as xml) as [Мое неотправленное сообщение], transmission_status from sys.transmission_queue

Мое неотправленное сообщение

transmission_status

<Preved />

The session keys for this conversation could not be created or accessed. The database master key is required for this operation.

Поэтому от греха лучше сделать

use tempdb

create master key encryption by password = 'AbraCadabra'

Скрипт 3

На случай повторного воспроизведения демки добавляю предварительную зачистку местности. Я-то все равно при написании поста гонял ее неоднократно, и для чистоты эксперимента приходилось каждый раз удалять все, что насоздавалось в процессе. Тип сообщения нельзя убить, покуда имеется хотя бы один использующий его контракт, а контракт - покуда есть хотя бы один использующий его сервис. Имена, начинающиеся с https://, в данном случае пижонство. Можно было начинать их с //Adventure-Works.com/ля-ля-ля, можно было просто назвать Sample_Contract.

use tempdb

if exists(select 1 from sys.services where name = 'Masha') drop service Masha

if exists(select 1 from sys.services where name = 'Dubrovsky') drop service Dubrovsky

if exists(select 1 from sys.service_contracts where name = 'https://blogs.msdn.com/alexejs/ContractMashi&Dubrovskogo')

drop contract [https://blogs.msdn.com/alexejs/ContractMashi&Dubrovskogo]

Скрипт 4

Создаем новый тип сообщения. Тип сообщения - это именованная декларация нашего намерения, что сообщение должно соответствовать определенному формату. Например, такой-то XSD. Тогда после VALIDATION = надо написать VALID_XML WITH SCHEMA COLLECTION и указать ее. Это то, что создается при помощи оператора CREATE XML SCHEMA COLLECTION. Можно просто сказать, что это будет любой правильный XML (WELL_FORMED_XML). Можно вообще сказать, что в этом сообщении ничего не будет (EMPTY), потому что уже название типа сообщения говорит получателю, что он должен сделать по его получении. Например, пример из BOL. Там создается тип сообщения что-то типа ShowMeYourSysprocesses, по получении которого получатель лезет в соответствующий DMV и отсылает результаты в ответ на это сообщение. Есть еще тип NONE. Означает, что написано. Никакой валидации, сообщение может нести в себе что угодно. Используется, когда в теле лежит не XML, а просто varbinary(max).

if exists(select 1 from sys.service_message_types where name = 'https://blogs.msdn.com/alexejs/MashinMessageType')

drop message type [https://blogs.msdn.com/alexejs/MashinMessageType]

create message type [https://blogs.msdn.com/alexejs/MashinMessageType] validation = well_formed_xml

Скрипт 5

Взаимодействие на основе сервис-брокера построено по принципу двухстороннего общения. Сторона, начинающая общение, называется инициатором, а тот, к кому она обращается, - TARGET. Можно оговорить, что любая сторона вправе начать общение (ANY). В контракте прописываются определенные ранее типы сообщений, которыми будут обмениваться стороны. Например, в ответ на сообщение типа ShowMeYourSysprocesses, посланное инициатором, target отвечает сообщением типа HereMyCurrentSysprocesses. На самом деле, кроме того, какое сообщение будет первым, контракт не задает никакого дальнейшего порядка обмена сообщениями или workflow. Сервис-брокер вообще не регулирует эти вещи. В ответ на просьбу показать системные процессы target может показать их, например, два раза.

create contract [https://blogs.msdn.com/alexejs/ContractMashi&Dubrovskogo]

(

 [https://blogs.msdn.com/alexejs/MashinMessageType] sent by initiator

)

Скрипт 6

Очередь - это место для хранения сообщений, которые ждут своей обработки. В очереди отправки находятся сообщения, ожидающие отправки, в очереди приема - пока с ними сделают что-нибудь осмысленное с точки зрения бизнес-смысла. Чтобы обеспечить гарантированную доставку, сервис-брокер отправителя помещает отправленное сообщение в служебную очередь transmission_queue, пока не получит подтверждения от получателя. В очередь приема на получателе сообщение попадает, когда сервис-брокер получателя удостоверится, что его доставка на уровне транспорта успешно завершена и тип валидный (отвечает типу, прописанному в контракте). Тогда он автоматически высылает отправителю подтверждение, и тот удаляет сообщение из transmission_queue. Читать из очереди можно оператором SELECT, как из таблицы, а читать и забирать - оператором RECEIVE. Опция RETENTION все равно позволяет сохранить все сообщения в очереди до завершения общения. Это делается обычно для нужд отладки или аудита. Опция ACTIVATION позволяет назначить обработчика, который делает над прочитанным из очереди сообщением что-либо осмысленное с точки зрения бизнес-смысла. Обработчиком может быть хранимая процедура на T-SQL или CLR или вообще какая-то внешняя программа (как в случае Event Notification). Обработчик нужен для того, чтобы срабатывать, как триггер, по приходу в очередь нового сообщения.

if exists(select 1 from sys.service_queues where name = 'QueueMashi') drop queue QueueMashi

create queue QueueMashi

if exists(select 1 from sys.service_queues where name = 'QueueDubrovskogo') drop queue QueueDubrovskogo

create queue QueueDubrovskogo

Скрипт 7

Под очереди на самом деле заводятся внутренние таблицы, которые можно видеть в sys.internal_tables. Например,

 

select * from sys.internal_tables where name like 'queue_messages_%'

покажет сейчас 5 табличек, соответствующих очередям: 2 пользовательские (QueueMashi и QueueDubrovskogo) и 3 служебные (QueryNotificationErrorsQueue, EventNotificationErrorsQueue, ServiceBrokerQueue).

 

Сервис-брокер построен по принципам SOA (сервис-ориентированной архитектуры), поэтому понятие очередей сообщений в нем отделено от понятия конечных точек, которые порождают эти сообщения и которым они, в конечном счете, предназначены. Эти конечные точки называются сервисами. Сервис садится на очередь и оговаривает контракт, по которому он готов общаться. На одной очереди может сидеть несколько сервисов.

create service Masha on queue QueueMashi ([https://blogs.msdn.com/alexejs/ContractMashi&Dubrovskogo])

create service Dubrovsky on queue QueueDubrovskogo ([https://blogs.msdn.com/alexejs/ContractMashi&Dubrovskogo])

Скрипт 8

Практически, все. На этом Маша готова отправлять сообщение Дубровскому. Абстракция, в рамках которой происходит общение (обмен сообщениями), называется беседой. Все сообщения отправляются в рамках какой-либо беседы. Как отмечалось выше, в настоящий момент беседа является синонимом диалога, т.е. двустороннего общения точка-точка. В теории, наверно, можно представить себе трилоги, когда между собой общаются три SQL Servera, вообще, n-логи, а также монологи, когда SQL Server вещает, ни к кому конкретно не обращаясь. Это уже после того, как они пообщались на троих. В книжках про 2005-й на эту тему писали "A monologue is a one-way conversation between a single publisher service and several subscriber services. Currently, monologues are not supported in Service Broker, but they may be included in a future version of Service Broker", откуда она перекочевала в книжки про 2008-й и перекочует в 2008 R2.

declare @ch uniqueidentifier

begin dialog conversation @ch from service Masha to service 'Dubrovsky' on contract [https://blogs.msdn.com/alexejs/ContractMashi&Dubrovskogo] with encryption = off

select @ch

declare @msg xml = '<Preved/>'

;send on conversation @ch message type [https://blogs.msdn.com/alexejs/MashinMessageType] (@msg)

--end conversation @ch

Скрипт 9

Смотрим, что-нибудь появилось в очереди получения?

select * from QueueDubrovskogo

Скрипт 10

Да, там сейчас одна строка, которая есть отправленное Машей сообщение. Колонка conversation_handle содержит гуид беседы, которая была открыта Машей в операторе begin dialog conversation, однако он не равен @ch, т.к. это гуид беседы, автоматически созданной в соответствие @ch на стороне таргета (Дубровского). Можно отправить ответ на сообщение в рамках той же беседы. Колонка message_sequence_number есть identity, которое будет монотонно возрастать с каждым новым сообщением, чтобы обеспечить тот же порядок поступления сообщений, что и при их отправке, в пределах данной беседы. Далее поля можно не пояснять: service_name, service_contract_name, message_type_name, - они знакомы нам по Скриптам 8, 6, 5. Мessage_body - это отправленный XML в двоичном виде:

select cast(message_body as xml) from QueueDubrovskogo

-------------------

<Preved />

(1 row(s) affected)

Скрипт 11

 

Receive отличается от select тем, что не только читает, но и убирает прочитанное сообщение из очереди:

declare @x xml

;receive top (1) @x = cast(message_body as xml) from QueueDubrovskogo

select @x

Скрипт 12

 

После этого очередь QueueDubrovskogo пустеет.

 

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

select ce.conversation_handle, ce.conversation_id, s.name, sq.name, ce.is_initiator, ce.state_desc from sys.conversation_endpoints ce join sys.services s on ce.service_id = s.service_id join sys.service_queues sq on s.service_queue_id = sq.object_id order by ce.conversation_id, ce.is_initiator desc

conversation_handle

conversation_id

name

name

is_initiator

state_desc

797EC6BC-9989-DE11-9E85-00155D000103

C9B186E3-3BAC-496E-9314-E16FD2217D2B

Masha

QueueMashi

1

CONVERSING

7C7EC6BC-9989-DE11-9E85-00155D000103

C9B186E3-3BAC-496E-9314-E16FD2217D2B

Dubrovsky

QueueDubrovskogo

0

CONVERSING

AEA01D3A-9D89-DE11-9E85-00155D000103

FE095A07-5120-469E-B511-F75650AB3758

Dubrovsky

QueueDubrovskogo

1

CONVERSING

B1A01D3A-9D89-DE11-9E85-00155D000103

FE095A07-5120-469E-B511-F75650AB3758

Masha

QueueMashi

0

CONVERSING

Скрипт 13

Первая строчка - это диалог, созданный в Скрипте 9. В момент выполнения begin dialog conversation ... в Скрипте 8 появляется только первая строчка со статусом STARTED_OUTBOUND. Описание состояний - см. BOL, sys.conversation_endpoints. Сторона таргета активизируется после того, как он получает первое сообщение от инициатора. Тогда таргет создает у себя свой conversation_handle в рамках того же conversation_id. Я об этом уже говорил после Скрипта 10. Состояние беседы меняется на CONVERSING. Третья строчка - это я ради хохмы послал <Отвед/> от Дубровского. В коде этот факт не отражен, потому что все делалось абсолютно тождественно Скрипту 9 с точностью до from service Dubrovsky to service 'Masha'. При этом был создан новый диалог, поэтому у них с предыдущей разные conversation_id. Четвертая строчка - это аналог второй для третьей. Если бы Дубровский послал ответ, указав conversation_handle из второй строчки, третьей бы (и, соответственно, четвертой) не появилось. Тема реюза диалогов является достаточно актуальной и постоянно обсуждается в форумах: https://www.sqlmonster.com/Uwe/Forum.aspx/sql-server-sb/146/sys-conversation-endpoints. Дело в том, что команды DROP CONVERSATION, подобной DROP QUEUE, CONTRACT, SERVICE, MESSAGE TYPE не существует, поэтому sys.conversation_endpoints имеет тенденцию незаметно со временем возрастать и достигать таких недеццких размеров, которые начинают не по-детски причинять беспокойство. Как написали такие уважаемые люди, как Роджер Волтер и Ремус Русану, существует фоновый процесс, который очищает диалоги приблизительно через полчаса после того, как они были закрыты - см. https://social.msdn.microsoft.com/Forums/en-US/sqlservicebroker/thread/3aa188df-db75-4d50-a278-1ab484245f3e. Им, конечно, видней, коль они писали сервис-брокер. Однако закрытие диалога является достаточно кропотливой процедурой. В принципе, при открытии диалога ему можно указать срок жизни при помощи опции LIFETIME, по истечении которой он присылает ошибку и самозакрывается, но если этого не сделать, то, чтобы правильно закрыть его руками, над ним нужно пошаманить с двух концов: https://social.msdn.microsoft.com/Forums/en-US/sqlservicebroker/thread/fa242e07-b42e-49fa-9c6f-70556e581210:

  • The transition CONVERSING->DISCONNECTED_OUTBOUND is when the initiator issues END CONVERSATION.
  • The transition CONVERSING->DISCONNECTED_INBOUND is when the EndDialog message sent by initiator arrives at the target
  • The transition DISCONNECTED_OUTBOUND->CLOSED is when the acknowledgement for the initiators EndDialog message comes back to it.
  • The transition DISCONNECTED_INBOUND->CLOSED is when the target issues END CONVERSATION in response to the EndDialog message received from initiator.

И после того, как он придет в состояние CLOSED, должно пройти еще полчаса прежде, чем процесс в бэкграунде приберет его. Утверждается, что это мера защиты от replay attacks, когда злобный хакер перехватывает сетевой трафик и тупо проигрывает его. Сохранение конечных точек на таргете не позволит пересоздать диалог путем проигрывания. Видимо, каждые полчаса у них генерится новый секьюрный токен. Как бы то ни было. Я не удержался и примкнул к сонму борцов за насильственную очистку диалогов:

select * from sys.conversation_endpoints

declare @conversation_handle uniqueidentifier = '00000000-0000-0000-0000-000000000000'

while 1 = 1 begin

select top 1 @conversation_handle = conversation_handle from sys.conversation_endpoints where conversation_handle > @conversation_handle /*and state <> 'CO'*/ order by conversation_handle

if @@rowcount = 0 break

end conversation @conversation_handle with cleanup

select @conversation_handle

end

select * from sys.conversation_endpoints

Скрипт 14

DMV sys.conversation_endpoints опустело. Либо его можно очистить еще так:

alter database tempdb set new_broker

Скрипт 15

Подобно disable_broker (см. Скрипт 1) она оставляет сервисы, очереди, контракты, типы сообщений, однако чистит диалоги.

Я собираюсь по-новой выполнить Скрипт 9, когда Маша отправляет Дубровскому сообщение <Превед/>, однако Дубровский будет отвечать ей, не начиная новый диалог со своей стороны, как в прошлый раз, а в рамках начатого Машей. Для этого нам нужно слегка модифицировать Скрипт 9. Дело в том, что по нынешнему контракту (см. Скрипт 6) отвечать на сообщения нельзя. В контракте прописано только sent by initiator. В прошлый раз Дубровский для ответа заводил свой новый диалог, то есть выступал в нем тоже как инициатор. Если он сейчас попытается ответить как таргет, в рамках существующего диалога, то немедленно получит по рукам:

Msg 8434, Level 16, State 1, Line 2

The message cannot be sent because the message type 'https://blogs.msdn.com/alexejs/MashinMessageType' is marked SENT BY INTITIATOR in the contract, however this service is a Target.

Команды ALTER CONTRACT в Т-SQL не предусмотрено, проще создать новый.

create contract NewContract

(

 [https://blogs.msdn.com/alexejs/MashinMessageType] sent by any

)  

Скрипт 16

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

declare @ch uniqueidentifier

begin dialog conversation @ch from service Masha to service 'Dubrovsky' on contract NewContract

declare @msg xml = '<Preved/>'

;send on conversation @ch message type [https://blogs.msdn.com/alexejs/MashinMessageType] (@msg)

А ответа нет. Дубровский клянется, что ничего не получал, и его очередь (select * from QueueDubrovskogo) пуста. Маша смотрит в диалоги (Скрипт 13), но вместо ожидаемых первых двух строчек резалтсета видит вот что:

conversation_handle

conversation_id

name

name

is_initiator

state_desc

8C3791DF-C089-DE11-9E85-00155D000103

8513E70B-A9E8-4E93-84CA-EBFABFF7014F

Masha

QueueMashi

1

ERROR

Скрипт 17

Печальная, она выполняет Скрипт 3, и видит, что ее сообщение действительно никуда не ушло, а застряло в sys.transmission_queue.

 

Мое неотправленное сообщение

transmission_status

<Preved />

Service Broker received an error message on this conversation. Service Broker will not transmit the message; it will be held until the application ends the conversation.

 

Очень информативно, ничего не скажешь. В чем же ошибка? Мы уже обшарили все: и очередь Дубровского, и список диалогов, и очередь неотправленных сообщений. Остается залезть к Маше в очередь:

select message_type_name, validation, cast(message_body as xml) from QueueMashi

message_type_name

validation

(No column name)

https://schemas.microsoft.com/SQL/ServiceBroker/Error

X

<Error xmlns="https://schemas.microsoft.com/SQL/ServiceBroker/Error"><Code>-8408</Code><Description>Target service 'Dubrovsky' does not support contract 'NewContract'.</Description></Error>

Скрипт 18

Вот, оказывается, в чем причина. Мы создали новый контракт и начали по нему общение, но сервисы (Скрипт 8) про него еще ничего не знают. Сервис Дубровский понимает только старый контракт https://blogs.msdn.com/alexejs/ContractMashi\&Dubrovskogo и отказывается принимать в свою очередь сообщение в рамках NewContract. К счастью, существует команда ALTER SERVICE:

alter service Masha (add contract NewContract)

alter service Dubrovsky (add contract NewContract)

Скрипт 19

 

Выполняем ее, очищаем предыдущие диалоги, потому что в рамках диалога в состоянии ER ничего отправить не удастся - будет ошибка The conversation endpoint is not in a valid state for SEND. Аналогичная ошибка возникает, когда та сторона уже закрыла диалог командой END

CONVERSATION, и он находится в состоянии DI (disconnected inbound). Остается тоже закрыть, чтобы не болтался. Повторяем Скрипт 17 и видим, что теперь все работает, как надо. В диалогах появился новый conversation_id = 53AC3195-19D3-4DD2-90D7-46F5A842F87B и под него создалось два conversation_handle = E606D522-C889-DE11-9E85-00155D000103 у Маши и E906D522-C889-DE11-9E85-00155D000103 у Дубровского со статусом CONVERSING. В очередь Дубровского упало Машино сообщение - см. второй резалтсет:

image001

рис.1

Теперь Дубровский может послать ответ в рамках той же беседы, указав соответствующий conversation_handle (E906D522-C889-DE11-9E85-00155D000103) из строки 2, который создался на его стороне по приходу Машиного письма. Этот conversation_handle можно взять из Машиного письма, т.к. он автоматически подставляется в его заголовок - см. колонку conversation_handle во втором резалтсете.

declare @ch uniqueidentifier = (select top 1 conversation_handle from QueueDubrovskogo order by message_sequence_number desc)

;send on conversation @ch message type [https://blogs.msdn.com/alexejs/MashinMessageType] (cast('<Отвед/>' as xml))

select * from sys.conversation_endpoints

select * from QueueMashi

select cast(message_body as xml) from QueueMashi

Скрипт 20

Отправляем <Отвед/> и видим, что Маша благополучно его получает к себе в очередь:

image003

Рис.2

Поскольку Дубровскому надоело вручную читать очереди, он создает процедуру автоматической обработки поступающих сообщений, которая, не мудрствуя лукаво, будет все их сваливать в таблицу tbl:

if object_id('tbl', 'U') is not null drop table tbl

create table tbl (dt datetime default sysdatetime(), body xml)

if object_id('ProcessMessagesMashi', 'P') is not null drop proc ProcessMessagesMashi

go

create proc ProcessMessagesMashi as

declare @ch uniqueidentifier, @msgtype sysname, @body varbinary(max)

while 1 = 1 begin

 waitfor (

   receive top(1) @ch = conversation_handle, @msgtype = message_type_name, @body = message_body from QueueDubrovskogo

 )

 if @msgtype = 'https://blogs.msdn.com/alexejs/MashinMessageType' insert tbl (body) values(cast(@body as xml))

 else end conversation @ch

end

go

Скрипт 21

и вешает ее на свою очередь, задействуя опцию активации

alter queue QueueDubrovskogo with activation

(

 status = on,

 procedure_name = ProcessMessagesMashi,

 max_queue_readers = 1,

 execute as self

)

Скрипт 22

Теперь, когда Маша присылает Дубровскому новое сообщение:

declare @ch uniqueidentifier

select @ch = ce.conversation_handle from sys.conversation_endpoints ce join sys.services s on ce.service_id = s.service_id join sys.service_queues sq on s.service_queue_id = sq.object_id where s.name = 'Masha' and ce.far_service = 'Dubrovsky' and ce.is_initiator = 1 and ce.state = 'CO'

;send on conversation @ch message type [https://blogs.msdn.com/alexejs/MashinMessageType] (cast('<Превед/>' as xml))

select * from tbl

Скрипт 23

мы видим, что они будут сразу по приходе извлекаться из очереди и перенаправляться в таблицу tbl. Согласно коду процедуры старые сообщения, болтавшиеся до этого в очереди Дубровского, также будут перенесены в таблицу, а очередь очищена.

image005

Рис.3

Процедура активизируется, как только в очереди что-либо обнаруживается. Повлиять на нее в дальнейшем, ставя всякие alter queue QueueDubrovskogo with activation ( status = off ), нельзя. Активизированные процедуры видны в

select * from sys.dm_broker_activated_tasks

spid

database_id

queue_id

procedure_name

execute_as

15

2

245575913

[dbo].[ProcessMessagesMashi]

1

Скрипт 24

Стандартной командой KILL можно прихлопнуть сессию, с которой выполняется активированная процедура: kill 15