Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Коллеги, на первый взгляд простая задача поставила в тупик.

Требуется:
Синхронизировать два асинхронных процесса по типу "Первый должен дождаться завершения второго"

Предпологаемое решение:
1 Первый генерит ключ, создает Второй процесс, отдает ему ключ, сам запоминает ключ
2 Два процесса начинают выполняться асинхронно, предпологается, что Второй работает дольше Первого.
3 Первый доходит до определенной точки, на его пути встречается "слушание" очереди (Queu) WAITFOR ( .... ) и он ожидает появление в очереди сообщения со своим ключом
4 Второй, когда выполнится, посылает сообщение в очередь с этим ключом
5 У Первого срабатывает нахождение этого сообщения с данным ключом и его выбрасывает из WAITFOR

Трудности:
1 Не могу явно записать в очередь этот ключ, разве что "зашить" в message_body
2 Не могу прочитать из очереди оператором RECEIVE сообщение с этим ключом, если оно находится в message_body

Вопросы:
1 Как преодолеть вышеперечисленные трудности?
2 Может есть изъян в схеме решения и можно предложить другое, более элегантное?
3 Подумываю над реализацией сего на простой таблице, где вместо WAITFOR будет крутится while пока не прочтет данный ключ из таблицы, который чуть позже вставит Второй процесс.
Насколько это решение менее эффективно по сравнению с использованием очередей? (хотя очень хочется реализовать на Queue)
21 фев 13, 09:40    [13958872]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
Ennor Tiegael
Member

Откуда:
Сообщений: 3422
max79,

В качестве ключа можно использовать conversation_handle. Точнее, conversation_id из представления sys.conversation_endpoints, т.к. идентификаторы диалога будут разные на разных концах диалога.
21 фев 13, 10:10    [13959051]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Ennor Tiegael спасибо большое, к сожалению в решении это не понадобилось,
цель достигнута, через Queue, но ценою утери чести (страшная затычка)
21 фев 13, 16:02    [13961970]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
Гость333
Member

Откуда:
Сообщений: 3683
max79,

Можете накидать тут скелет вашего решения на T-SQL? Возможно, что-то удастся исправить, чтобы всё было красиво.
21 фев 13, 16:08    [13962003]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Гость333 Спасибо большое за проявленный интерес и во имя стремления к истине потрачу время и набросаю решение, может быть Вы привнесете элегантность в некоторые моменты
Окончательного пока кода нет - это тестовые наброски, но которые работают:

1 Процессы в одной точке:
-- insert first message just for define dialog_handle and conversation_handle
  
  DECLARE @Dialog UNIQUEIDENTIFIER
  DECLARE @Valve  UNIQUEIDENTIFIER = newid()
  
    BEGIN DIALOG  CONVERSATION @Dialog
     FROM SERVICE  alr_Q_SRC_SERVICE
       TO SERVICE 'alr_Q_DST_SERVICE'
       ON CONTRACT alr_Q_CONTRACT
     WITH ENCRYPTION = OFF;


    SEND ON CONVERSATION @Dialog
    MESSAGE TYPE alr_Q_MESSAGE_TYPE( cast( @Valve as varchar(128) ) );
    
 declare @handle varchar(128) set @handle = (select conversation_handle from alr_Q_DST where cast(message_body as varchar(128) ) = @Valve )
 select @Dialog, @handle
 
 -- Reset the message in the Queue which no need more...
 waitfor(
 receive * from alr_Q_DST where conversation_handle = @handle
 )


Теперь первый процесс должен запомнить conversation_handle ( @handle )
Второй должен запомнить dialog_handle (@Dialog)
дале могут разбегаться по своим делам

2 Первый процесс прибегает к клапану в виде waitfor и слушает его с принесенным с собой conversation_handle ( @handle )
waitfor(
receive top(1) cast(message_body as varchar(100)) from alr_Q_DST where conversation_handle = @handle
)

3 Второй процесс когда поделает свои дела должен послать уведомление об открытии клапана в рамках диалога Ключ которого принес собой
    SEND ON CONVERSATION @Dialog
    MESSAGE TYPE alr_Q_MESSAGE_TYPE( 'some shit' );


4 Это все, увидив в очереди сообщение для себя Первый процесс срывается с цепи-waitfor и может забирать подготовленные для него данные Вторым процессом
21 фев 13, 23:46    [13964062]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
Ennor Tiegael
Member

Откуда:
Сообщений: 3422
Только мне кажется, что @Valve в данном случае лишний?

Ну и вот это
where cast(message_body as varchar(128) ) = @Valve
на активной очереди с достаточным количеством сообщений убьет все к чертям.

Нормально вы сделали, в общем. Другой вопрос, что доп. критерий координации потоков вам не нужен. Родительский поток в процессе создания дочернего может передать ему соотв. conversation_id в качестве параметра. А может и не передавать - если потомок полностью автономен, то он просто получает нужные параметры из message_body, генерит данные и отправляет их обратно в тот же самый диалог, из которого он "возник".

Все, даже связь родитель-потомок не нужна, она у вас уже есть - это диалог.
22 фев 13, 00:07    [13964151]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Здравствуйте Ennor Tiegael

Ennor Tiegael
Ну и вот это
where cast(message_body as varchar(128) ) = @Valve

на активной очереди с достаточным количеством сообщений убьет все к чертям.


"- Сам нехачу, щто не надо?" (Кавказская пленница) Более чем с Вами согласен. Хотя интенсивность загрузки очереди будет и небольшой (не более 20 записей в еденицу времени в очереди) но решение действительно плохое.
Тут мы и подходим к ключевому моменту проблемы. Все эти кретинские телодвижения и призваны лишь только для того, чтобы связать dialog_handle и conversation_handle. Потому что когда начинаешь диалог
BEGIN DIALOG  CONVERSATION @Dialog

то после посыла сообщения
    SEND ON CONVERSATION @Dialog

нет НИКАКОЙ связи между @Dialog и образовавшимся сообщением в очереди и соответственно с sys.conversation_endpoints для получения conversation_handle. А связь безусловно есть, но я не смог найти такую сис-вьюху. Может Вы откроете секрет? Тогда затычка @Valve станет действительно не нужной.
Спасибо!
22 фев 13, 08:25    [13964688]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
Ennor Tiegael
Member

Откуда:
Сообщений: 3422
max79,

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

А вообще - Receive (Transact-SQL), example B. Только в запрос TOP (1) добавить осталось.
22 фев 13, 09:10    [13964835]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Ennor Tiegael
Вы когда-нибудь содержимое очередей селектить пробовали?


В свою очередь у Вас спрошу. А Вы пробовали? Укажите пожалуйста где в записи очереди хранится сгенерированный @dialog, или где он вообще хранится.
22 фев 13, 11:29    [13965655]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
Ennor Tiegael
Member

Откуда:
Сообщений: 3422
max79
Ennor Tiegael
Вы когда-нибудь содержимое очередей селектить пробовали?


В свою очередь у Вас спрошу. А Вы пробовали? Укажите пожалуйста где в записи очереди хранится сгенерированный @dialog, или где он вообще хранится.
Да, и много раз. Внутри очередь - это по сути та же таблица, но с некоторыми дополнительными ограничениями. При этом селект, в отличие от ресива, не удаляет сообщения из очереди, так что можете не опасаться потери данных.
select top (1) q.conversation_handle, q.message_type_name, q.message_body
from dbo.alr_Q_DST q;
Вот первое же выбираемое поле и есть ваш идентификатор диалога, если вам без него так жизнь не мила.

У меня такое ощущение, что вы не до конца понимаете собственную идею разделяемого выполнения. Можете огласить задачу в бизнес-терминах? Я просто в упор не понимаю, зачем вашему второму потоку знать, кто именно его родитель.
22 фев 13, 11:41    [13965748]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Ennor Tiegael
У меня такое ощущение, что вы не до конца понимаете собственную идею разделяемого выполнения.

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

Ennor Tiegael
Вот первое же выбираемое поле и есть ваш идентификатор диалога, если вам без него так жизнь не мила.

Выполните скрипт и проанализируйте результаты
  DECLARE @Dialog UNIQUEIDENTIFIER
  
    BEGIN DIALOG  CONVERSATION @Dialog
     FROM SERVICE  alr_Q_SRC_SERVICE
       TO SERVICE 'alr_Q_DST_SERVICE'
       ON CONTRACT alr_Q_CONTRACT
     WITH ENCRYPTION = OFF;

    SEND ON CONVERSATION @Dialog
    MESSAGE TYPE alr_Q_MESSAGE_TYPE( 'someshit' );
    
select @Dialog
select * from alr_Q_DST  where conversation_handle = @Dialog
select top(1) * from alr_Q_DST -- In the queue may be exists there are many messages already.
select * from alr_Q_DST


Как вы можете после создания сообщения в очереди узнать его @Dialog = conversation_handle ??? Никак, для этого и была вставлена неприятная затычка с @Valve. В последстви этим @Dialog = conversation_handle должны ползоваться оба процесса, Первый слушать сообщение, а второй послать это сообщение.

Ennor Tiegael
Можете огласить задачу в бизнес-терминах?


Помойму первоначальный вопрос и содержал постановку задачи.
22 фев 13, 13:07    [13966415]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Между прочим я проверил, Ваше утверждение, что conversation_handle это и есть идентификатор диалога(@Dialog) - неправильно
22 фев 13, 13:22    [13966573]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
Ennor Tiegael
Member

Откуда:
Сообщений: 3422
max79,

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

Когда вы начинаете диалог на инициаторе, то у вас создается пара записей в sys.conversation_endpoints - одна для инициатора, другая для таргета. Если диалог происходит в пределах одной БД, то вы увидите обе эти записи вместе, но в общем случае они могут быть порознь друг от друга, в разных базах на разных серверах.
Значение поля conversation_handle у этих записей БУДЕТ разным - это сделано by design. Связываются же эти записи по значению поля conversation_id - вот оно у них будет одинаковым, неважно насколько далеко разнесены участники диалога. Именно поэтому, когда вы смотрите указатель диалога на приемнике, он никогда не совпадает с тем, что вы получили на инициаторе (conversation_handle, когда сообщение уже в очереди - это и есть сторона приемника).

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

Больше всего меня интересует момент, описанный у вас как
автор
1 Первый генерит ключ, создает Второй процесс
Я не знаю, как именно вы собираетесь создавать второй процесс - в MSSQL не очень много способов сделать fork execution, но их, тем не менее, больше одного. И мне кажется, что сервис брокер для этого один из наиболее подходящих. Как бы это сделал я:

0. Логика второго процесса (далее - потомок) оформляется в виде хранимой процедуры. Если нужно, она может иметь параметры, указывающие ей, что именно нужно сделать. Собственно говоря, разницы с обычной хранимкой, вызываемой напрямую, здесь нет вообще. Ключевой момент здесь - входных параметров должно быть достаточно, чтобы полностью автономно выполнить свою часть работы.
1. Исходный процесс (далее - родитель) открывает диалог и посылает в него сообщение соотв. типа, в теле которого содержатся значения параметров для процедуры-потомка. Я обычно использую XML вида <Root ParamName="ParamValue" ... />
2. Процедура активации на стороне очереди-приемника подхватывает это сообщение, парсит его тело и вызывает процедуру-потомка, передав ей параметры из тела сообщения. Понятно, что выполнение процедуры активации дальше не пойдет - она будет ждать завершения вычислений, но нам это и не важно, т.к. она уже выполняется в отдельном от нас spid'е.
3. Потомок выполняет свою часть работы и возвращает результаты в процедуру активации. Можно вернуть рекордсет, который будет пойман снаружи посредством insert into ... exec ..., а можно вернуть output parameter - тот же XML, как вариант. В данном случае вам виднее, что именно у вас там за результат и как его лучше передать. Процедура активации при необходимости оборачивает результат в XML и отправляет его обратно в тот же самый диалог - нужный ей для этого conversation_handle у нее есть, она получила его, когда забирала сообщение из очереди.
4. Процесс-родитель заканчивает свою часть работы и ставит waitfor (receive top (1) ...) на очередь-инициатор (это важно! не перепутайте). Когда потомок завершает свою работу, его результат свалится именно туда. Причем это произойдет в рамках того же самого диалога, который был изначально открыт родителем. Вам даже не придется никакой дополнительной фильтрации указывать в этом ресиве - брокер все сделает за вас. Получив нужные данные, родитель вызывает end conversation @Dialog, соотв. системное сообщение упадет в процедуру активации на стороне приемника, и эта процедура сделает аналогичный end conversation, но уже на своей стороне.

Все. Приемнику не нужно знать никакие идентификаторы инициатора, и наоборот. Вся необходимая информация передается в теле исходного сообщения.
22 фев 13, 16:20    [13967925]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
max79
Member

Откуда: Ташкент
Сообщений: 18
Ennor Tiegael
Я наверно попрошу у Вас прощение за излишнюю эмоциональность высказанную выше, боле того Вы никак на нее не отреагировали, за что дополнительный респект.
Поверьте, когда Вы в первый раз написали про sys.conversation_endpoints, я безусовно все там исследовал, и честное слово - следов сгенерированного @Dialog я там не обнаружил. Попробую внимательно прочитать, то что Вы написали и продолжить исследования, но не сегодня, сегодня наступает час ветеранов Советской армии ::--))

P.S. Мне довольно затруднительно описать задачу более подробно, если Вас интересует что тама на практике, я с удовольствием пообщаюсь с Вами в скайпе - maxuzi79
22 фев 13, 18:40    [13968623]     Ответить | Цитировать Сообщить модератору
 Re: Работа с очередями (Queue ServiceBroker )  [new]
Ennor Tiegael
Member

Откуда:
Сообщений: 3422
max79,

You're welcome :) Надеюсь, вам это поможет.
22 фев 13, 18:44    [13968651]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить