Compartilhar via


Tratando de mensagens suspeitas

Este tópico descreve como um aplicativo que usa o Service Broker pode detectar uma mensagem suspeita e removê-la da fila sem depender na detecção automática de mensagem suspeita.

O Service Broker fornece detecção automática de mensagens suspeitas. A detecção automática de mensagem suspeita definirá o status da fila como OFF, se uma transação que recebe mensagens for revertida cinco vezes. Esse recurso fornece uma proteção contra falhas catastróficas que um aplicativo não pode detectar programaticamente. Porém, um aplicativo não deve depender desse recurso para o 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 de seu aplicativo, pode ser normal as mensagens falharem de vez em quando. Por exemplo, em um aplicativo de entrada de pedido, o serviço que processa um pedido pode tomar menos tempo de processamento 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. Talvez o serviço possa 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 despesa que não contém um nome de funcionário ou um número de ID do funcionário não poderá 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 processá-la. 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 tratar de um alto volume de mensagens onde cada mensagem tem uma vida útil limitada, ele poderá ser mais eficiente se remover imediatamente qualquer mensagem que faça com que a operação falhe. Por exemplo, se a mensagem fornecer um relatório de processo do serviço de destino, o serviço inicial poderá optar por descartar um relatório de progresso em branco, 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. Porém, para alguns aplicativos, preservar o conteúdo da mensagem pode ser apropriado.

  • Seu aplicativo deverá registrar outras informações sobre a falha? Em algumas casos, você pode querer localizar outras informações sobre a conversa. Por exemplo, você pode usar o exibição do catálogo sys.conversation_endpoints para identificar a instância do Service Broker remoto que gerou a mensagem suspeita.

  • 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 suspeita 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 embora uma mensagem falhe. Por exemplo, um serviço que recebe dados de inventário de um chão de armazém pode receber, ocasionalmente, uma mensagem com um número de peça desconhecido. Em vez de terminar a conversa, o serviço pode salvar a mensagem em uma tabela separada para um operador inspecionar mais tarde.

Exemplo: Detectando uma mensagem suspeita

Esse exemplo de Transact-SQL mostra um serviço simples, sem-estado que inclui lógica para tratar mensagens suspeitas. Antes de o procedimento armazenado receber uma mensagem, o procedimento salvará a transação. Quando o procedimento não puder processar uma mensagem, o procedimento reverterá a transação para o ponto em que foi salva. 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 UNIQUEIDENTIFIER ;
      DECLARE @messageBody VARBINARY(MAX) ;
      DECLARE @messageTypeName 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 TRANSACTION ;
          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 NVARCHAR(MAX) ;
            SET @expenseReport = CAST(@messageBody AS NVARCHAR(MAX)) ;
            EXEC AdventureWorks2008R2.dbo.AddExpenseReport
              @report = @expenseReport ;
            IF @@ERROR <> 0
             BEGIN
               ROLLBACK TRANSACTION UndoReceive ;
               EXEC TrackMessage @conversationHandle ;
             END ;
            ELSE
             BEGIN
               EXEC 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 ;
            EXEC dbo.ClearMessageTracking @conversationHandle ;
          END ;


         COMMIT TRANSACTION ;
    END ;
END ;

O procedimento armazenado TrackMessage localiza o número de vezes que uma mensagem falhou. Quando a mensagem não tiver falhado antes, o procedimento inserirá um contador novo 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 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
      EXEC 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 contém simplesmente uma coluna conversation_handle e uma coluna count, conforme mostrado no exemplo a seguir:

CREATE TABLE ExpenseServiceFailedMessages (
  conversation_handle uniqueidentifier PRIMARY KEY,
  count smallint
) ;

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

CREATE PROCEDURE ClearMessageTracking
  @conversationHandle uniqueidentifier
AS
BEGIN
   DELETE FROM dbo.ExpenseServiceFailedMessages
     WHERE conversation_handle = @conversationHandle ;
END ;
GO

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 seu aplicativo mantiver o estado, ele poderá ser mais eficiente para incluir as informações de rastreamento para mensagens de falha nas tabelas de estado para o aplicativo.

Os procedimentos armazenados anteriormente não tratam erros que causam a falha de 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 ocorrer cinco vezes, a detecção automática de mensagem suspeita 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ê acreditar que o processamento executado na mensagem pode gerar a falha da transação, você poderá usar as instruções TRY e CATCH para tratar do erro. Para obter mais informações sobre como tratar erros, consulte Controlando erros do Mecanismo de Banco de Dados.