포이즌 메시지 처리

이 항목에서는 Service Broker를 사용하는 응용 프로그램에서 포이즌 메시지 자동 감지 기능을 사용하지 않고 포이즌 메시지를 검색한 다음 큐에서 제거하는 한 가지 방법을 설명합니다.

Service Broker는 포이즌 메시지를 자동으로 감지합니다. 포이즌 메시지 자동 감지 기능은 큐에서 메시지를 받는 트랜잭션이 5번 롤백되면 큐 상태를 OFF로 설정합니다. 이 기능을 사용하면 응용 프로그램에서 프로그래밍 방식으로 감지할 수 없는 치명적인 오류로부터 보호할 수 있습니다. 하지만 일반적인 처리에 이 기능만 사용해서는 안 됩니다. 포이즌 메시지 자동 감지 기능은 큐를 중지하므로 이 기능을 사용하면 포이즌 메시지가 제거될 때까지 응용 프로그램의 모든 처리가 사실상 중단됩니다. 대신 응용 프로그램에서는 응용 프로그램 논리의 일부로 포이즌 메시지를 감지하고 제거해야 합니다.

이 섹션에서 설명하는 전략은 메시지가 일정 횟수 실패할 경우 제거된다고 가정합니다. 많은 응용 프로그램의 경우 이 가정이 적절하지만 응용 프로그램에 이 전략을 사용하기 전에 다음 사항을 확인하는 것이 좋습니다.

  • 실패 횟수가 응용 프로그램에 안정적인 수준입니까? 응용 프로그램에 따라 이따금씩 메시지가 실패하는 것은 정상적일 수 있습니다. 예를 들어 주문 입력 응용 프로그램에서 주문을 처리하는 서비스는 새 고객 레코드를 추가하는 서비스보다 처리 시간이 짧을 수 있습니다. 이 경우 새 고객에 대한 주문은 즉시 처리되지 않을 수 있으며 이는 정상적입니다. 응용 프로그램에서 메시지가 포이즌 메시지인지 여부를 결정할 때는 이러한 지연 시간을 고려하여 메시지를 처리하기 전에 몇 번의 실패는 허용해야 할 수 있습니다.

  • 응용 프로그램에서 메시지 내용을 검사하여 해당 메시지가 성공할 수 없음을 확인하는 기능이 빠르고 안정적입니까? 그렇다면 프로그램에서 메시지를 처리하는 데 실패한 횟수를 고려하는 것보다 이 방법을 사용하는 것이 더 좋습니다. 예를 들어 직원 이름이나 직원 ID 번호가 없는 경비 보고서는 처리할 수 없습니다. 이 경우 오류로 인해 처리할 수 없는 메시지를 처리하려고 시도하기보다는 해당 메시지에 즉시 응답하는 것이 프로그램을 보다 효율적으로 사용할 수 있는 방법입니다. 다른 유효성 검사도 고려해야 합니다. 예를 들어 ID 번호가 있지만 음수와 같이 할당된 번호 범위를 벗어나는 경우에는 응용 프로그램에서 대화를 즉시 종료할 수 있습니다.

  • 실패 후 메시지를 제거해야 합니까? 응용 프로그램에서 대용량의 메시지를 처리하며 각 메시지가 유용한 수명이 제한적인 경우 작업이 실패하도록 하는 모든 메시지를 즉시 제거하는 것이 가장 효율적입니다. 예를 들어 메시지가 대상 서비스에서 진행률 보고서를 제공하는 경우 시작하는 서비스에서는 메시지 처리 없이 받기를 커밋하여 빈 진행률 보고서를 삭제하도록 선택할 수 있습니다. 이 경우 대화는 계속됩니다.

응용 프로그램에서 포이즌 메시지를 처리할 방법을 결정할 때는 다음 사항을 확인해야 합니다.

  • 응용 프로그램에서 메시지 실패 및 해당 내용을 로깅해야 합니까? 대부분의 경우에는 로깅이 필요하지 않습니다. 하지만 일부 응용 프로그램에서는 메시지 내용을 보존하는 것이 적절할 수 있습니다.

  • 응용 프로그램에서 실패에 대한 다른 정보를 로깅해야 합니까? 일부 경우에는 대화에 대한 다른 정보를 추적해야 할 수 있습니다. 예를 들어 카탈로그 뷰 sys.conversation_endpoints를 사용하여 포이즌 메시지를 생성한 원격 Broker 인스턴스를 확인할 수 있습니다.

  • 응용 프로그램에서 오류가 발생한 대화를 종료해야 합니까? 아니면 서비스 계약에서 응용 프로그램이 대화를 닫지 않고 오류를 나타낼 수 있도록 허용해야 합니까? 대부분의 서비스에서 포이즌 메시지를 받는다는 것은 해당 계약에 설명된 태스크를 완료할 수 없다는 것을 의미합니다. 이 경우 응용 프로그램에서는 오류가 발생한 대화를 종료합니다. 다른 경우에는 한 메시지가 실패하더라도 대화를 계속해야 할 수 있습니다. 예를 들어 창고의 재고 데이터를 받는 서비스에서는 경우에 따라 알 수 없는 부품 번호가 포함된 메시지를 받을 수 있습니다. 이 경우 서비스에서는 대화를 종료하지 않고 운영자가 나중에 조사할 수 있도록 해당 메시지를 별도의 테이블에 저장할 수 있습니다.

예: 포이즌 메시지 감지

이 Transact-SQL 예에서는 포이즌 메시지를 처리하기 위한 논리를 포함하는 간단한 상태 비저장 서비스를 보여 줍니다. 저장 프로시저는 메시지를 받기 전에 트랜잭션을 저장했다가 메시지를 처리할 수 없을 때 해당 트랜잭션을 저장 지점으로 롤백합니다. 부분 롤백은 메시지의 대화 그룹에 대한 잠금을 계속 유지하면서 메시지를 큐로 돌려 보냅니다. 해당 프로그램에서는 대화 그룹 잠금을 계속 유지하므로 실패한 메시지 목록이 저장된 테이블을 업데이트할 수 있으며, 다른 큐 판독기에서 해당 메시지를 처리할 위험은 없습니다.

다음 예에서는 응용 프로그램의 활성화 저장 프로시저를 정의합니다.

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 ;

저장 프로시저 TrackMessage는 메시지가 실패한 횟수를 추적합니다. 이전에 메시지가 실패한 적이 없으면 이 프로시저는 메시지에 대한 새 카운터를 ExpenseServiceFailedMessages 테이블에 삽입합니다. 또는 카운터를 통해 메시지가 실패한 횟수를 확인합니다. 이 프로시저는 카운터가 미리 정의된 횟수보다 작을 경우 카운터를 늘리고, 카운터가 미리 정의된 횟수보다 클 경우에는 오류가 발생한 대화를 종료하고 테이블에서 해당 대화의 카운터를 제거합니다.

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

ExpenseServiceFailedMessages 테이블의 정의에는 다음 예에 표시된 것과 같이 conversation_handle 열과 count 열만 포함되어 있습니다.

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

다음 예제에 표시된 것과 같이 ClearMessageTracking 프로시저는 ExpenseServiceFailedMessages 테이블에서 대화의 카운터를 삭제합니다.

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

여기에서 보여 주는 전략은 매우 간단합니다. 요구 사항에 맞는 응용 프로그램을 작성하려면 이 항목에 설명된 내용을 기초로 삼아야 합니다. 예를 들어 응용 프로그램에서 상태를 저장하는 경우에는 실패한 메시지에 대한 추적 정보를 응용 프로그램의 상태 테이블에 포함하는 것이 더 효율적일 수 있습니다.

위의 저장 프로시저는 트랜잭션 실패의 원인이 될 수 있는 오류는 처리하지 않습니다. 이 서비스가 전체 트랜잭션 실패의 원인이 될 수 있는 메시지를 받으면 해당 트랜잭션은 롤백됩니다. 롤백이 5번 발생하면 포이즌 메시지 자동 감지 기능은 해당 큐 상태를 OFF로 설정합니다. 이 경우 다른 응용 프로그램이나 관리자가 포이즌 메시지를 제거해야 합니다.

메시지에 대해 수행하는 처리 작업으로 인해 트랜잭션이 실패하는 것으로 생각되는 경우에는 TRY 및 CATCH 문을 사용하여 오류를 처리할 수 있습니다. 오류 처리에 대한 자세한 내용은 데이터베이스 엔진 오류 처리를 참조하십시오.