Share via


Gestione di messaggi non elaborabili

In questo argomento viene descritto un modo in cui un'applicazione che utilizza Service Broker può rilevare un messaggio non elaborabile e rimuoverlo dalla coda senza basarsi sul rilevamento automatico dei messaggi non elaborabili.

Service Broker esegue il rilevamento automatico dei messaggi non elaborabili. In base a questa funzionalità lo stato della coda viene impostato su OFF se viene eseguito per cinque volte il rollback di una transazione che riceve i messaggi dalla coda. Sebbene questa funzionalità rappresenti uno strumento di protezione da errori irreversibili che non è possibile rilevare a livello di programmazione, un'applicazione non deve basarsi su tale funzionalità durante la normale elaborazione. Poiché il rilevamento automatico dei messaggi non elaborabili interrompe la coda, questa funzionalità arresta in effetti tutta l'elaborazione relativa all'applicazione fino a quando il messaggio non elaborabile non viene rimosso. Al contrario, un'applicazione deve tentare di rilevare e di rimuovere i messaggi non elaborabili come parte della logica dell'applicazione stessa.

La strategia descritta in questa sezione presuppone che un messaggio debba essere rimosso se si verifica un errore per un numero di volte specifico. Sebbene per molte applicazioni questa ipotesi sia valida, prima di applicare questa strategia nell'applicazione utilizzata considerare i seguenti aspetti:

  • Esistenza di un conteggio degli errori affidabile per l'applicazione. In base all'applicazione, può essere normale che talvolta nei messaggi si verifichi un errore. In un'applicazione per la registrazione di ordini, ad esempio, il tempo necessario al servizio che elabora un ordine può essere minore di quello impiegato dal servizio che aggiunge un nuovo record del cliente. In questo caso, l'impossibilità di elaborare immediatamente un ordine per un nuovo cliente può risultare normale. Per decidere se un messaggio è non elaborabile o meno, l'applicazione deve tenere conto del ritardo. Può essere necessario che il servizio consenta numerosi errori prima di rimuovere il messaggio.

  • Capacità dell'applicazione di controllare rapidamente e in maniera affidabile il contenuto di un messaggio per rilevare che non è mai possibile eseguirlo in modo corretto. In una situazione di questo tipo tale strategia risulta migliore rispetto al conteggio del numero di volte che il programma non è stato in grado di elaborare il messaggio. Una nota di spesa che non contiene un nome di dipendente oppure un numero ID dipendente ad esempio non può essere elaborato. In questo caso, il programma può essere più efficiente se risponde immediatamente a un messaggio che non può essere elaborato restituendo un errore anziché tentare di elaborare il messaggio. È opportuno inoltre considerare anche un'altra convalida. Se ad esempio il numero ID è presente, ma non è compreso nell'intervallo di numeri assegnati (ad esempio se è un numero negativo), l'applicazione può terminare immediatamente la conversazione.

  • Necessità di rimuovere un messaggio dopo qualsiasi errore. Se l'applicazione gestisce un volume elevato di messaggi e se a ogni messaggio è associata una durata utile limitata, può essere più efficiente rimuovere immediatamente qualsiasi messaggio che impedisce l'esecuzione di un'operazione. Se il messaggio fornisce un report di stato dal servizio di destinazione, il servizio di origine può scegliere di ignorare un report di stato vuoto eseguendo il commit della ricezione senza elaborare il messaggio. In questo caso, la conversazione continua.

Quando si decide il modo in cui l'applicazione gestisce un messaggio non elaborabile considerare i seguenti aspetti:

  • Necessità per l'applicazione di registrare l'errore e il contenuto del messaggio. Sebbene in molti casi questa operazione non sia necessaria, per alcune applicazioni può risultare appropriato mantenere il contenuto del messaggio.

  • Necessità per l'applicazione di registrare altre informazioni relative all'errore. In alcuni casi, può essere necessario registrare altre informazioni sulla conversazione. È possibile ad esempio utilizzare la vista del catalogo sys.conversation_endpoints per identificare l'istanza di Service Broker remota che ha generato il messaggio non elaborabile.

  • Necessità per l'applicazione di terminare la conversazione restituendo un errore o necessità per il contratto relativo al servizio di consentire a un'applicazione di indicare un errore senza chiudere la conversazione. Per molti servizi la ricezione di un messaggio non elaborabile significa che l'attività descritta nel contratto non può essere completata. In questo caso, l'applicazione termina la conversazione restituendo un errore. In altri casi, la conversazione potrebbe essere in grado di continuare anche se nel messaggio si verifica un errore. Un servizio che riceve dati relativi alle scorte da un reparto di magazzino può ricevere ad esempio occasionalmente un messaggio con un numero di articolo sconosciuto. Anziché terminare la conversazione, il servizio può salvare il messaggio in una tabella separata affinché un operatore lo verifichi in un momento successivo.

Esempio: rilevamento di un messaggio non elaborabile

In questo esempio Transact-SQL viene illustrato un servizio semplice e senza stato (stateless) che include la logica per la gestione di messaggi non elaborabili. Prima che la stored procedure riceva un messaggio, la procedura salva la transazione. Quando non è in grado di elaborare un messaggio, la procedura esegue il rollback della transazione nel punto di salvataggio. Il rollback parziale restituisce il messaggio alla coda e contemporaneamente continua a mantenere un blocco sul gruppo di conversazioni relativo al messaggio. Poiché il programma continua a mantenere il blocco del gruppo di conversazioni, può aggiornare una tabella che gestisce un elenco di messaggi in cui si è verificato un errore senza il rischio che un altro agente di lettura coda possa elaborare il messaggio.

Nell'esempio seguente viene definita la stored procedure di attivazione per l'applicazione.

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 AdventureWorks.dbo.AddExpenseReport
              @report = @expenseReport ;
            IF @@ERROR <> 0
             BEGIN
               ROLLBACK TRANSACTION UndoReceive ;
               EXEC TrackMessage @conversationHandle ;
             END ;
            ELSE
             BEGIN
               EXEC AdventureWorks.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 ;

La stored procedure TrackMessage tiene traccia del numero di volte che in un messaggio si è verificato un errore. Se nel messaggio non si è verificato alcun errore in precedenza, la procedura inserisce un nuovo contatore per il messaggio nella tabella ExpenseServiceFailedMessages. In caso contrario, la procedura controlla il contatore in relazione al numero di volte che nel messaggio si è verificato un errore e lo incrementa quando il relativo valore è minore di un numero predefinito. Quando il contatore è maggiore del numero predefinito, la procedura termina la conversazione restituendo un errore e rimuove il contatore relativo alla conversazione dalla tabella.

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

La definizione della tabella ExpenseServiceFailedMessages contiene semplicemente una conversation_handle colonna e una count colonna, come illustrato nell'esempio seguente:

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

La procedura ClearMessageTracking elimina il contatore per una conversazione dalla tabella ExpenseServiceFailedMessages, come illustrato nell'esempio seguente:

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

La strategia illustrata è intenzionalmente semplice. È consigliabile utilizzare le idee presentate in questo argomento come base per la creazione di un'applicazione corrispondente alle proprie esigenze. Se ad esempio l'applicazione mantiene le informazioni sullo stato, può risultare più efficiente includere le informazioni di rilevamento per i messaggi in cui si è verificato un errore nelle tabelle di stato relative all'applicazione.

Le stored procedure precedenti non gestiscono errori che potrebbero impedire l'esecuzione di una transazione. Se questo servizio riceve un messaggio che impedisce l'esecuzione dell'intera transazione, verrà eseguito il rollback della transazione stessa. Se questa situazione si verifica cinque volte, il rilevamento automatico dei messaggi non elaborabili imposterà lo stato della coda su OFF. In questo caso, il messaggio non elaborabile deve essere rimosso da un'applicazione diversa o da un amministratore.

Se si ritiene che l'elaborazione eseguita sul messaggio potrebbe provocare un errore della transazione, è possibile utilizzare le istruzioni TRY e CATCH per gestire l'errore. Per ulteriori informazioni sulla gestione degli errori, vedere Gestione degli errori del Motore di database.