Behandeln von nicht verarbeitbaren Nachrichten

In diesem Thema wird eine Möglichkeit beschrieben, wie eine Anwendung, die Service Broker verwendet, eine nicht verarbeitbare Nachricht erkennen und aus der Warteschlange entfernen kann, ohne auf die automatische Erkennung von nicht verarbeitbaren Nachrichten zurückzugreifen.

Service Broker stellt die automatische Erkennung von nicht verarbeitbaren Nachrichten bereit. Bei der automatischen Erkennung nicht verarbeitbarer Nachrichten wird der Warteschlangenstatus auf OFF festgelegt, wenn für eine Transaktion, die Nachrichten aus der Warteschlange empfängt, fünfmal ein Rollback ausgeführt wird. Dieses Feature bietet Schutz gegen schwerwiegende Fehler, die eine Anwendung nicht programmgesteuert erkennen kann. Für die normale Verarbeitung sollte eine Anwendung jedoch nicht auf dieses Feature zurückgreifen. Da die Warteschlange durch die automatische Erkennung von nicht verarbeitbaren Nachrichten beendet wird, hält dieses Feature die gesamte Verarbeitung für die Anwendung an, bis die nicht verarbeitbare Nachricht entfernt wird. Stattdessen sollte eine Anwendung nicht verarbeitbare Nachrichten möglichst als Teil der Anwendungslogik erkennen und entfernen.

Die in diesem Abschnitt beschriebene Strategie setzt voraus, dass eine Nachricht entfernt wird, wenn sie nach einer bestimmten Anzahl von Versuchen weiterhin nicht verarbeitet werden kann. Diese Annahme ist für viele Anwendungen gültig. Beachten Sie aber vor Verwendung dieser Strategie in der Anwendung die folgenden Fragen:

  • Ist eine Fehlerzählung für die Anwendung zuverlässig? Je nach Anwendung ist es eventuell normal, dass Nachrichten bisweilen nicht verarbeitet werden können. In einer Anwendung für die Auftragserfassung benötigt beispielsweise der Dienst, der einen Auftrag verarbeitet, weniger Verarbeitungszeit als der Dienst, durch den ein neuer Kundendatensatz hinzugefügt wird. In diesem Fall ist es möglicherweise normal, dass ein Auftrag für einen neuen Kunden nicht sofort verarbeitet werden kann. Die Anwendung muss diese Verzögerung berücksichtigen, wenn sie bestimmt, ob es sich bei der Nachricht um eine nicht verarbeitbare Nachricht handelt. Der Dienst muss vor dem Entfernen der Nachricht möglicherweise mehrere Fehler zulassen.

  • Kann die Anwendung schnell und zuverlässig den Inhalt einer Nachricht überprüfen, um festzustellen, ob sie immer fehlschlägt? Falls ja, ist es besser, diese Strategie zu verwenden, als zu zählen, wie oft das Programm die Nachricht nicht verarbeiten konnte. Beispielsweise kann ein Ausgabenbericht, der keinen Mitarbeiternamen bzw. keine Mitarbeiter-ID enthält, nicht verarbeitet werden. In diesem Fall ist das Programm möglicherweise effizienter, wenn es sofort auf eine Nachricht reagiert, die nicht mit einem Fehler verarbeitet werden kann, als wenn es versucht, die Nachricht zu verarbeiten. Berücksichtigen Sie auch andere Überprüfungen. Falls beispielsweise die ID-Nummer zwar vorhanden ist, aber außerhalb des Bereichs der zugewiesenen Zahlen liegt (z. B. eine negative Zahl), kann die Anwendung die Konversation sofort beenden.

  • Sollten Sie eine Nachricht nach jedem Fehler entfernen? Wenn die Anwendung ein großes Nachrichtenvolumen behandelt, bei dem jede Nachricht eine begrenzte Lebensdauer hat, besteht die effizienteste Methode möglicherweise in der Entfernung aller Nachrichten, die zum Fehlschlagen einer Operation führen. Falls die Nachricht beispielsweise einen Statusbericht des Zieldiensts bereitstellt, verwirft der initiierende Dienst eventuell einen leeren Statusbericht, indem er einen Commit für den Empfang ausführt, ohne die Nachricht zu verarbeiten. In diesem Fall wird die Konversation fortgesetzt.

Beachten Sie die folgenden Fragen, wenn Sie festlegen, wie die Anwendung nicht verarbeitbare Nachrichten behandelt:

  • Soll die Anwendung den Fehler und den Inhalt der Nachricht protokollieren? In vielen Fällen ist dies nicht notwendig. Bei einigen Anwendungen ist die Beibehaltung des Nachrichteninhalts aber möglicherweise angemessen.

  • Soll die Anwendung weitere Informationen zum Fehler protokollieren? In einigen Fällen sollten möglicherweise weitere Informationen zur Konversation nachverfolgt werden. Sie könnten beispielsweise mithilfe der Katalogsicht sys.conversation_endpoints die Remotebrokerinstanz identifizieren, die die nicht verarbeitbare Nachricht erzeugt hat.

  • Soll die Anwendung die Konversation mit einem Fehler beenden, oder soll der Vertrag für den Dienst zulassen, dass eine Anwendung einen Fehler angeben kann, ohne die Konversation zu beenden? Für viele Dienste bedeutet der Empfang einer nicht verarbeitbaren Nachricht, dass der im Vertrag beschriebene Task nicht abgeschlossen werden kann. In diesem Fall beendet die Anwendung die Konversation mit einem Fehler. In anderen Fällen kann die Konversation möglicherweise fortgesetzt werden, obwohl eine Nachricht nicht verarbeitet werden kann. Ein Dienst, der Bestandsdaten aus einer Lagerhalle empfängt, erhält möglicherweise gelegentlich eine Nachricht mit einer unbekannten Teilenummer. Statt die Konversation zu beenden, kann der Dienst die Nachricht in einer separaten Tabelle speichern, damit sie zu einem späteren Zeitpunkt durch das zuständige Personal überprüft wird.

Beispiel: Erkennen von nicht verarbeitbaren Nachrichten

Dieses Transact-SQL-Beispiel zeigt einen einfachen Dienst ohne Status, der Logik zum Behandeln von nicht verarbeitbaren Nachrichten umfasst. Bevor die gespeicherte Prozedur eine Nachricht empfängt, speichert sie die Transaktion. Wenn die Prozedur eine Nachricht nicht verarbeiten kann, führt sie für die Transaktion einen Rollback zum Sicherungspunkt aus. Der teilweise Rollback sorgt dafür, dass die Nachricht in die Warteschlange zurückgegeben und zugleich die Konversationsgruppensperre für die Nachricht beibehalten wird. Da das Programm die Konversationsgruppensperre aufrechterhält, kann es eine Tabelle mit einer Liste fehlerhafter Nachrichten aktualisieren, ohne dass die Gefahr besteht, dass ein anderer Warteschlangenleser die Nachricht verarbeiten könnte.

Im folgenden Beispiel wird die gespeicherte Aktivierungsprozedur für die Anwendung definiert:

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 ;

Die gespeicherte Prozedur TrackMessage verfolgt nach, wie oft eine Nachricht nicht verarbeitet werden konnte. Wenn die Nachricht zuvor keine Fehler verursacht hat, fügt die Prozedur einen neuen Leistungsindikator für die Nachricht in die ExpenseServiceFailedMessages-Tabelle ein. Andernfalls überprüft die Prozedur den Leistungsindikator für die Anzahl der erfolglosen Verarbeitungsversuche für die Nachricht. Die Prozedur inkrementiert den Leistungsindikator, wenn er kleiner als eine vordefinierte Zahl ist. Ist der Leistungsindikator größer als die vordefinierte Zahl, beendet die Prozedur die Konversation mit einem Fehler und entfernt den Leistungsindikator der Konversation aus der Tabelle.

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

Die Definition der ExpenseServiceFailedMessages-Tabelle enthält lediglich eine conversation_handle-Spalte und eine count-Spalte, wie das folgende Beispiel veranschaulicht:

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

Die ClearMessageTracking-Prozedur löscht den Leistungsindikator für ein Konversation aus der ExpenseServiceFailedMessages-Tabelle, wie das folgende Beispiel veranschaulicht:

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

Die hier gezeigte Strategie ist mit Absicht einfach gehalten. Nutzen Sie die Konzepte in diesem Thema als Grundlage zum Erstellen einer auf Ihre Bedürfnisse zugeschnittenen Anwendung. Wenn Ihre Anwendung beispielsweise Statusinformationen verwaltet, ist es möglicherweise effizienter, die Nachverfolgungsinformationen für fehlerhafte Nachrichten in die Statustabellen für die Anwendung aufzunehmen.

Die oben aufgeführten gespeicherten Prozeduren behandeln keine Fehler, die zum Fehlschlagen einer Transaktion führen würden. Falls dieser Dienst eine Nachricht empfängt, die dazu führt, dass die gesamte Transaktion fehlschlägt, wird ein Rollback für die Transaktion ausgeführt. Wenn dies fünfmal geschieht, legt die automatische Erkennung nicht verarbeitbarer Nachrichten den Warteschlangenstatus auf OFF fest. In diesem Fall müssen die nicht verarbeitbaren Nachrichten von einer anderen Anwendung oder von einem Administrator entfernt werden.

Falls Sie davon ausgehen, dass die für die Nachricht ausgeführte Verarbeitung zu einem Transaktionsfehler führen könnte, können Sie diesen mithilfe von TRY-Anweisungen und CATCH-Anweisungen behandeln. Weitere Informationen zum Behandeln von Fehlern finden Sie unter Behandeln von Fehlern des Datenbankmoduls.