Manipular mensagens suspeitas

Aplica-se a:SQL ServerInstância Gerenciada de SQL do Azure

Este artigo descreve uma maneira que um aplicativo que usa o Service Broker pode detectar uma mensagem venenosa e remover a mensagem da fila sem depender da detecção automática de mensagens suspeitas.

O Service Broker fornece detecção automática de mensagens suspeitas. A detecção automática de mensagens suspeitas define o status da fila para OFF se uma transação que recebe mensagens da fila reverter cinco vezes. Esse recurso fornece uma proteção contra falhas catastróficas que um aplicativo não pode detectar programaticamente. No entanto, um aplicativo não deve depender desse recurso para processamento normal. Como a detecção automática de mensagem suspeita interrompe a fila, esse recurso paralisa efetivamente todo o processamento do aplicativo até a mensagem suspeita ser removida. Em vez disso, um aplicativo deve tentar detectar e remover mensagens suspeitas como parte da lógica do aplicativo.

A estratégia descrita nesta seção assumirá que uma mensagem deve ser removida, se falhar um determinado número de vezes. Para muitos aplicativos, esse pressuposto é válido. Porém, antes de usar essa estratégia em seu aplicativo, considere as perguntas a seguir:

  • Uma contagem de falha é confiável para seu aplicativo? Dependendo do aplicativo, pode ser normal que as mensagens falhem de tempos em tempos. Por exemplo, em um aplicativo de entrada de pedido, o serviço que processa um pedido pode levar menos tempo de processamento do que o serviço que adiciona um novo registro de cliente. Nesse caso, pode ser normal que um pedido para um novo cliente não possa ser processado imediatamente. O aplicativo precisa considerar o atraso ao decidir se uma mensagem é suspeita ou não. O serviço pode precisar permitir várias falhas antes de remover a mensagem.

  • Seu aplicativo pode verificar o conteúdo de uma mensagem para detectar, de forma rápida e segura, se ele nunca obterá sucesso? Nesse caso, essa estratégia é melhor do que contar o número de vezes que o programa não processou a mensagem. Por exemplo, um relatório de despesas que não contém um nome de funcionário ou um número de ID do funcionário não pode ser processado. Nesse caso, o programa poderá ser mais eficiente se responder imediatamente a uma mensagem que não pode ser processada com um erro, em vez de tentar processar a mensagem. Considere também outra validação. Por exemplo, se o número de ID estiver presente, mas estiver fora do intervalo de números atribuídos (por exemplo, um número negativo), o aplicativo poderá terminar a conversa imediatamente.

  • Você deve remover uma mensagem depois de qualquer falha? Se o aplicativo manipular um alto volume de mensagens em que cada mensagem tem uma vida útil limitada, talvez seja mais eficiente remover imediatamente qualquer mensagem que faça com que uma operação falhe. Por exemplo, se a mensagem fornecer um relatório de progresso do serviço de destino, o serviço de início poderá optar por descartar um relatório de progresso vazio confirmando o recebimento sem processar a mensagem. Nesse caso, a conversa continua.

Considere as perguntas a seguir quando decidir como o aplicativo tratará uma mensagem suspeita:

  • Seu aplicativo deverá registrar a falha e o conteúdo da mensagem? Em muitos casos, isso não é necessário. No entanto, para alguns aplicativos, a preservação do conteúdo da mensagem pode ser apropriada.

  • Seu aplicativo deverá registrar outras informações sobre a falha? Em alguns casos, talvez você queira acompanhar outras informações sobre a conversa. Por exemplo, você pode usar a exibição sys.conversation_endpoints de catálogo para identificar a instância do agente remoto que gerou a mensagem de veneno.

  • Seu aplicativo deve terminar a conversa com um erro ou o contrato para o serviço deve permitir que um aplicativo indique um erro sem fechar a conversa? Para muitos serviços, receber uma mensagem venenosa significa que a tarefa descrita no contrato não pode ser concluída. Nesse caso, o aplicativo termina a conversa com um erro. Em outros casos, a conversa pode continuar mesmo que uma mensagem falhe. Por exemplo, um serviço que recebe dados de inventário de um chão de armazém pode ocasionalmente receber uma mensagem com um número de parte desconhecido. Em vez de encerrar a conversa, o serviço pode salvar a mensagem em uma tabela separada para um operador inspecionar posteriormente.

Exemplo: detectar uma mensagem de veneno

Esse exemplo de Transact-SQL mostra um serviço simples, sem-estado que inclui lógica para tratar de mensagens suspeitas. Antes de o procedimento armazenado receber uma mensagem, o procedimento salvará a transação. Quando o procedimento não pode processar uma mensagem, o procedimento reverte a transação para o ponto de salvamento. A reversão parcial retorna a mensagem à fila, enquanto continua mantendo um bloqueio no grupo de conversa para a mensagem. Como o programa continua mantendo o bloqueio do grupo de conversa, o programa poderá atualizar uma tabela que mantém uma lista de mensagens com falha sem o risco de outro leitor de fila poder processar a mensagem.

O exemplo a seguir define o procedimento armazenado de ativação para o aplicativo:

CREATE PROCEDURE ProcessExpenseReport
AS
BEGIN
    WHILE (1 = 1)
        BEGIN
            BEGIN TRANSACTION;
            DECLARE @conversationHandle AS UNIQUEIDENTIFIER;
            DECLARE @messageBody AS VARBINARY (MAX);
            DECLARE @messageTypeName AS NVARCHAR (256);

            SAVE TRANSACTION UndoReceive;

            WAITFOR (
                RECEIVE TOP (1) @messageTypeName = message_type_name,
                @messageBody = message_body,
                @conversationHandle = conversation_handle
                FROM ExpenseQueue),
            TIMEOUT 500;
            IF @@ROWCOUNT = 0
                BEGIN
                    ROLLBACK;
                    BREAK;
                END

            -- Typical message processing loop: dispatch to a stored
            -- procedure based on the message type name.  End conversation
            -- with an error for unknown message types.

            -- Process expense report messages. If processing fails,
            -- roll back to the save point and track the failed message.
            IF (@messageTypeName = '//Adventure-Works.com/AccountsPayable/ExpenseReport')
                BEGIN
                    DECLARE @expenseReport AS NVARCHAR (MAX);
                    SET @expenseReport = CAST (@messageBody AS NVARCHAR (MAX));
                                        EXECUTE AdventureWorks2008R2.dbo.AddExpenseReport
                        @report = @expenseReport;
                    IF @@ERROR <> 0
                        BEGIN
                            ROLLBACK TRANSACTION UndoReceive;
                            EXECUTE TrackMessage @conversationHandle;
                        END
                    ELSE
                        BEGIN
                            EXECUTE AdventureWorks2008R2.dbo.ClearMessageTracking @conversationHandle;
                        END
                END
            ELSE

            -- For error messages and end dialog messages, end the
            -- conversation.
            IF (@messageTypeName = 'https://schemas.microsoft.com/SQL/ServiceBroker/Error'
                OR @messageTypeName = 'https://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
                BEGIN
                    END CONVERSATION @conversationHandle;
                    EXECUTE dbo.ClearMessageTracking @conversationHandle;
                END
            COMMIT TRANSACTION;
        END
END

O procedimento TrackMessage armazenado rastreia o número de vezes que uma mensagem falhou. Quando a mensagem não falha antes, o procedimento insere um novo contador para a mensagem na tabela ExpenseServiceFailedMessages. Caso contrário, o procedimento verificará o contador quanto ao número de vezes que a mensagem falhou. O procedimento incrementa o contador quando o contador for menor que um número predefinido. Quando o contador for maior que o número predefinido, o procedimento terminará a conversa com um erro e removerá o contador para a conversa da tabela.

CREATE PROCEDURE TrackMessage (
    @conversationHandle UNIQUEIDENTIFIER
)
AS
BEGIN
    IF @conversationHandle IS NULL
        RETURN;
    DECLARE @count AS INT;
    SET @count = NULL;
    SET @count = (SELECT COUNT
                  FROM dbo.ExpenseServiceFailedMessages
                  WHERE conversation_handle = @conversationHandle);
    IF @count IS NULL
        BEGIN
            INSERT INTO dbo.ExpenseServiceFailedMessages (COUNT, conversation_handle)
            VALUES (1, @conversationHandle);
        END
    IF @count > 3
        BEGIN
            EXECUTE dbo.ClearMessageTracking @conversationHandle;
            END CONVERSATION @conversationHandle
                WITH ERROR = 500 DESCRIPTION = 'Unable to process message.';
        END
    ELSE
        BEGIN
            UPDATE dbo.ExpenseServiceFailedMessages
                SET COUNT = COUNT + 1
            WHERE conversation_handle = @conversationHandle;
        END
END
GO

A definição da tabela ExpenseServiceFailedMessages simplesmente contém uma conversation_handle coluna e uma count coluna, conforme mostrado no exemplo a seguir:

CREATE TABLE ExpenseServiceFailedMessages
(
    conversation_handle UNIQUEIDENTIFIER PRIMARY KEY,
    COUNT SMALLINT
);

O procedimento ClearMessageTracking exclui o contador de uma conversa da tabela ExpenseServiceFailedMessages, conforme mostrado no exemplo a seguir:

CREATE PROCEDURE ClearMessageTracking (
    @conversationHandle UNIQUEIDENTIFIER
)
AS
BEGIN
    DELETE dbo.ExpenseServiceFailedMessages
    WHERE conversation_handle = @conversationHandle;
END
GO

Observação

Os exemplos de código neste artigo foram testados usando o AdventureWorks2025 banco de dados de exemplo, que pode ser baixado na home page de Exemplos do Microsoft SQL Server e Projetos da Comunidade .

A estratégia mostrada aqui é deliberadamente simples. Você deve usar as ideias deste tópico como uma base para criar um aplicativo que atenda a suas necessidades. Por exemplo, se o aplicativo mantiver o estado, talvez seja mais eficiente incluir as informações de acompanhamento de mensagens com falha nas tabelas de estado do aplicativo.

Os procedimentos armazenados anteriores não lidam com erros que causam falha em uma transação. Se esse serviço receber uma mensagem que faça com que a transação inteira falhe, essa transação será revertida. Se isso acontecer cinco vezes, a detecção automática de mensagens suspeitas definirá o status da fila como OFF. Nesses casos, a mensagem suspeita deve ser removida por um aplicativo diferente ou por um administrador.

Se você acredita que o processamento executado na mensagem pode causar uma falha na transação, você pode usar TRY e CATCH instruções para lidar com o erro. Para obter mais informações sobre como tratar os erros, confira Compreendendo os erros do Mecanismo de Banco de Dados.