Должна ли процедура из очереди сообщений установить цикл? - PullRequest
0 голосов
/ 05 апреля 2019

Я пытаюсь начать использовать SQL Server Service Broker для аудита асинхронных действий пользователей приложения интрасети.

Я создал сообщение, контракт, очередь и сервис. Настройте процедуру запуска при активации этой очереди.

Пока все хорошо. Сообщения отправляются и принимаются.

Процедура получает сообщение Top 1 из этой очереди и выполняет все необходимые действия (в основном вставка в таблицу) и завершается.

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

1 Ответ

2 голосов
/ 05 апреля 2019

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

Таким образом, если ваша очередь заполняется быстрее, чем ваша процедура завершает свою работу, вы начнете видеть, что необработанные сообщения начинают накапливаться в очереди.

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

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

create procedure [dbo].[ssb_QProcessor_MyQueue]
with execute as owner as

set nocount, ansi_nulls, ansi_padding, ansi_warnings, concat_null_yields_null, quoted_identifier, arithabort on;
set numeric_roundabort, xact_abort, implicit_transactions off;

declare @Handle uniqueidentifier, @MessageType sysname, @Body xml;
declare @Error int, @ErrorMessage nvarchar(2048), @ProcId int = @@procid;


-- Fast entry check for queue contents
if not exists (select 0 from dbo.MySBQueueName with (nolock))
    return;

while exists (select 0 from sys.service_queues where name = 'MySBQueueName' and is_receive_enabled = 1) begin

    begin try
    begin tran;

    -- Receive something, if any
    waitfor (
        receive top (1) @Handle = conversation_handle,
            @MessageType = message_type_name,
            @Body = message_body
        from dbo.MySBQueueName
    ), timeout 3000;

    if @Handle is null begin
        -- Empty, get out
        rollback;
        break;
    end;

    -- Whatever processing logic you have should be put here



    commit;
    end try
    begin catch

    if nullif(@Error, 0) is null
        select @Error = error_number(), @ErrorMessage = error_message();

    -- Check commitability of the transaction
    if xact_state() = -1
        rollback;
    else if xact_state() = 1
        commit;

    -- Try to resend the message again (up to you)
    exec dbo.[ssb_Poison_Retry] @MessageType = @MessageType, @MessageBody = @Body,
        @ProcId = @ProcId, @ErrorNumber = @Error, @ErrorMessage = @ErrorMessage;

    end catch;

    -- Reset dialog handle
    select @Handle = null, @Error = null, @ErrorMessage = null;

end;

-- Done!
return;
...