SQL Server Service Broker: как структурировать диалоги для простого сценария очереди?

Я новичок в SQL Server Service Broker, и я пытаюсь понять лучший способ настроить Service Broker для (казалось бы) простого случая использования: я хочу создать простую рабочую очередь, где одно приложение сбрасывает рабочие элементы в очередь, а отдельное приложение подбирает рабочие элементы из этой очереди и обрабатывает их. Для первого приложения нет необходимости получать сообщения о состоянии от второго. Я хочу, чтобы очередь жила в одном экземпляре Sql Server.

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

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

каковы будут последствия прохождения любого из этих маршрутов?

кроме того, в первом случае мне кажется, что мне нужно сделать некоторые конечные разговоры, чтобы Sql Server мог очищать ресурсы внутри. Есть ли рекомендации, когда будет правильным куда их положить? (Или потенциально может быть лучше полагаться на то, что разговоры в конечном итоге выйдут?)

2 ответов


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

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

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

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

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


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

во-первых, помещение всех рабочих элементов в один разговор, вероятно, вызовет проблемы параллелизма, если у вас есть несколько потоков/процессов обработки рабочих элементов. Рабочие процессы Service broker обычно выглядят следующим образом (в псевдокод):

begin transaction
receive top n work items from queue
process work items
commit transaction

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

проблема параллелизма возникает из-за того, что Service broker запрограммирован таким образом, что каждая команда RECEIVE получает эксклюзивную блокировку чтения для всех сообщений в очереди, которые совместно используют тот же диалог (или группу разговоров), что и те это было получено. Эта блокировка удерживается до фиксации транзакции. (См.Блокировки Групп.) Таким образом, если все рабочие элементы в очереди находятся в одном диалоге, то, пока один рабочий процесс находится на шаге "рабочие элементы процесса", никакие другие рабочие процессы не могут выполнять какую-либо работу.

вторая проблема с помещением большого количества элементов в один разговор заключается в том, что он увеличивает количество рабочих элементов, которые вы можете потерять или должны повторно обработать при определенной ошибке условия. Чтобы описать это должным образом, я полагаюсь на Рема; посмотрите на его Обработка Разговоры, особенно часть, в которой говорится "повторное использование одного диалога для отправки всех ваших сообщений [...] это как класть все яйца в одну корзину."Возможно, вы сможете оправиться от некоторых из этих ситуаций с ошибками, но это, вероятно, усложнит ваш код.

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

это не значит, что правильное решение - всегда начинать отдельный разговор для каждого рабочего элемента. Однако после прочтения сообщений Ремуса его совет кажется разумным; начните с одного рабочего элемента за разговор, а затем добавьте сложность, если это необходимо. (Но, вероятно, ни в коем случае не следует доходить до крайности, помещая все сообщения в одном разговоре.)