Смекни!
smekni.com

MSSQL 2005 (Yukon) – работа с очередями и асинхронная обработка данных (стр. 2 из 7)

И еще несколько общих ограничений механизма output:

Секционированные представления и удаленные таблицы не могут быть источником output.

В случае оператора INSERT источником output не могут быть view.

Порядок записей, выдаваемых output, не гарантируется.

Если вызов output происходит в триггере, и вывод из output не перенаправляется в таблицу, то, очевидно, опция disallow results from triggers не должна быть установлена, в противном случае произойдет исключение.

Также, если не происходит перенаправления вывода output, то изменяемая таблица не должна иметь активных триггеров на данную операцию модификации. Например, если происходит INSERT c output, без перенаправления вывода в таблицу, то триггеров на INSERT быть не должно, хотя UPDATE и DELETE триггеры вполне могут быть.

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

Как ясно из названия данной функциональности, Microsoft предлагает использовать ее для работы с очередями. Во-первых, конструкцию DELETE … OUTPUT удобно применять для разгребания очереди, выполняя чтение и удаление прочитанной записи одним движением.

DELETE FROM output_test OUTPUT deleted.*

В комбинации с хинтом READPAST, можно организовать разгребание очереди из нескольких потоков одновременно, если есть такая необходимость. Ранее для этого приходилось использовать как минимум два запроса с явным блокированием нужных записей.

Во-вторых, механизм output можно использовать и для формирования очередей. В данном случае это сильно напоминает работу обычного триггера, в задачу которого входит складывать измененные данные в отдельную таблицу. Но триггер будет срабатывать для всех изменений без исключения, а output можно использовать только в определенных DML-операторах. Иными словами, если таблица может изменяться из двух мест, то триггер будет срабатывать в обоих случаях, а output можно использовать только для одного из них. Да и использовать output, наверное, будет проще, чем триггер.

Применить данный механизм с пользой можно и в отрыве от очередей. Как уже упоминалось ранее, результат output возвращается после подсчета значений по умолчанию, identity и вычисляемых столбцов, и это может оказаться довольно полезным.

Однако основную проблему коммуникации между асинхронными процессами такой подход не решает – это всего лишь небольшой синтаксический сахарок, несколько облегчающий работу с собственноручно написанными очередями, но не более того. В идеале же механизм обмена, как уже говорилось, должен обеспечивать транзакционность, отсутствие дубликатов, автоматическую работу с очередями, обработку групп сообщений, гарантировать очередность и т.д… Все это богатство было реализовано и вошло в следующую версию SQL Server под именем Service Broker.

Service Broker

При первом взгляде на Service Broker возникает вопрос: "А что во что, собственно, встроено, СУБД в подсистему сообщений или наоборот. :)"

С одной стороны, полноценная подсистема работы с сообщениями должна иметь собственное надежное хранилище, без этого невозможно реализовать все богатство, описанное в предыдущем разделе. И ребята из Редмонда с присущим им размахом решили, что раз СУБД у них уже есть, то почему бы ей не послужить в роли хранилища данных для подсистемы рассылки сообщений? С другой же стороны, использовать SQL Server исключительно в роли хранилища для одной подсистемы, пусть и очень мощной, тоже как-то неправильно... Так и родилось то, что мы исследуем сейчас: без SQL Server-а работа подсистемы сообщений невозможна, но при наличии подсистемы сообщений SQL Server перестает быть просто хранилищем данных, как это было в недавнем прошлом.

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

СОВЕТКнига, целиком посвященная Service Broker и выходящая в самое ближайшее время, называется The Rational Guide To SQL Server 2005 Service Broker Beta Preview (http://www.mannpublishing.com/Catalog/BookDetail.aspx?BookID=37). Она написана Роджером Уолтером (Roger Wolter), основная работа которого заключается как раз в руководстве группой, разрабатывающей этот самый Service Broker. Так что лучше него вряд ли кто-нибудь об этом механизме расскажет. :)

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

Работа с Service Broker-ом реализована через набор объектов, которые управляются посредством обычных DDL операторов CREATE, ALTER, DROP - ничего нового. Команды по работе с этими объектами также являются небольшим DML расширением T-SQL. Например, команда получения сообщения возвращает обычный реляционный набор данных и мало чем отличается от SELECT, так что тут не должно быть никаких сложностей. В посылке и получении сообщения участвуют следующие объекты (здесь приведены далеко не все, лишь необходимый минимум):

QUEUE (очередь): Service Broker использует очереди для того, чтобы не было зависимости между отправителем и получателем сообщения. Отправитель просто помещает сообщение в очередь и идет заниматься своими делами, не дожидаясь получателя, а доставку сообщения возлагает на плечи собственно Service Broker-а, будучи уверенным, что тот справится. Получатель же может забрать и обработать сообщение, когда ему будет удобно, зная, что его деятельность никоим образом не влияет на эффективность работы отправителя, а все сообщения выстроены в должном порядке. При этом есть возможность запустить несколько «получателей» одновременно, добиваясь тем самым параллельной обработки очереди для достижения большей эффективности.

DIALOG (диалог): Одной из частей подсистемы доставки сообщений является диалог. Эту конструкцию можно рассматривать как двунаправленный поток (stream) между двумя конечными точками, по которому курсируют сообщения. Все сообщения в диалоге добираются до получателя ровно в том порядке, в котором были отосланы отправителем. Этот порядок сохраняется, невзирая ни на какие сбои, транзакции, архивирования базы, погодные условия, курс доллара и прочие внезапные факторы.

На самом деле, диалог является частным случаем общения (conversation). Conversation – это не объект, а более низкоуровневое понятие – постоянный, надежный канал связи. В MSSQL 2005 диалог – единственный тип общения, но в следующих версиях обещают добавить monolog (монолог), однонаправленный поток один-ко-многим, и, возможно, что-то еще. Однако в текущей версии диалог и общение можно считать синонимами.

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

MESSAGE TYPE (тип сообщения): Любое сообщение должно быть ассоциировано с определенным типом. Это метка, которая передается вместе с сообщением и позволяет получателю понять, какого типа сообщение к нему приехало. По желанию, если сообщение представляет собой XML, то метка может быть ассоциирована с произвольной XML-схемой. В этом случае при получении производится проверка соответствия сообщения этой схеме, и если сообщение проверку не проходит, то оно отвергается.

CONTRACT (контракт): Контракт – это набор типов сообщений, при этом для каждого типа сообщения в контракте указывается, кому из участников диалога сообщение данного типа может принадлежать: отправителю, получателю или им обоим. Каждый диалог обязательно ассоциируется с контрактом. Иными словами, если тип сообщения не соответствует ни одному типу из контракта, ассоциированного с диалогом, то передать такое сообщение по этому диалогу не получится.

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

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

Отправитель и получатель – достаточно условные понятия, они имеют смысл только на этапе создания канала общения. После того, как канал создан, сообщения могут свободно гулять в обоих направлениях. Иными словами, отправитель – это та сторона, которая создает диалог. При этом само создание диалога не вызывает создания физического канала связи, канал создается лишь в тот момент, когда необходимо доставить первое сообщение, и удерживается до тех пор, пока диалог не завершится.

Через сервис отправителя сообщение попадает в очередь отправителя, если соответствует одному из типов сообщения, упомянутых в контракте. После этого оно передается в очередь получателя, где при получении также проходит проверку, после чего забирается оттуда собственно получателем. Если же получатель желает как-то ответить, то он в свою очередь формирует сообщение и отправляет его через свой сервис, и оно путешествует обратно тем же манером.

Теперь самое время приступить к практическим экспериментам. Для начала создадим все необходимые объекты:

-- тип сообщения, просто текст, для простоты безо всяких проверок и xml--CREATE MESSAGE TYPE [TestType] VALIDATION = NONE -- теперь можно создать контракт, разрешающий сообщения этого типа -- длялюбойизсторон--CREATE CONTRACT [TestContract] ([TestType] SENT BY ANY)-- для отправляющей стороны необходимо создать очередь -- и сервис на основе этой очереди--CREATE QUEUE [SourceQueue]CREATE SERVICE [SourceService] ON QUEUE [SourceQueue]-- Для принимающей стороны так же нужно создать принимающую очередь-- и принимающий сервис, причем принимающий сервис обязательно-- должен иметь контракт, хотя для отправляющего это не обязательно--CREATE QUEUE [TargetQueue]CREATE SERVICE [TargetService] ON QUEUE [TargetQueue] ([TestContract])

Все, объекты готовы. Теперь можно приступать собственно к передаче сообщения. В данном примере рассматривается самый простой вариант: из отправляющего сервиса сообщение попадает в очередь получателя и забирается оттуда.