다음을 통해 공유


MSMQ 4.0의 포이즌 메시지 처리

MSMQ4 샘플은 서비스에서 포이즌 메시지 처리를 수행하는 방법을 보여 줍니다. 이 샘플은 거래된 MSMQ 바인딩 샘플을 기반으로 합니다. 이 샘플은 netMsmqBinding를 사용합니다. 서비스는 대기 중인 메시지를 수신하는 서비스를 관찰할 수 있도록 하는 자체 호스팅 콘솔 애플리케이션입니다.

대기 중인 통신에서 클라이언트는 큐를 사용하여 서비스와 통신합니다. 보다 정확하게 말하자면, 클라이언트는 큐에 메시지를 보냅니다. 서비스는 큐에서 메시지를 받습니다. 따라서 서비스와 클라이언트는 큐를 사용하여 통신하기 위해 동시에 실행될 필요가 없습니다.

포이즌 메시지는 메시지를 읽는 서비스가 메시지를 처리할 수 없으므로 메시지를 읽는 트랜잭션을 종료할 때 큐에서 반복적으로 읽는 메시지입니다. 이러한 경우 메시지가 다시 시도됩니다. 메시지에 문제가 있는 경우 이론적으로 영원히 계속될 수 있습니다. 이 문제는 트랜잭션을 사용하여 큐에서 읽고 서비스 작업을 호출하는 경우에만 발생할 수 있습니다.

MSMQ 버전에 따라 NetMsmqBinding은 포이즌 메시지의 제한된 탐지에서부터 전체 탐지까지 지원합니다. 메시지가 포이즌으로 검색된 후 여러 가지 방법으로 처리할 수 있습니다. 또한 MSMQ 버전에 따라 NetMsmqBinding은 포이즌 메시지의 제한된 처리에서 전체 처리까지 지원합니다.

이 샘플에서는 Windows Server 2003 및 Windows XP 플랫폼에서 제공되는 제한된 포이즌 시설과 Windows Vista에서 제공되는 전체 포이즌 시설을 보여 줍니다. 두 샘플 모두 독 메시지를 대기열에서 다른 대기열로 이동하는 것이 목표입니다. 그런 다음 해당 큐는 포이즌 메시지 서비스에 의해 처리될 수 있습니다.

MSMQ v4.0 포이즌 처리 샘플

Windows Vista에서 MSMQ는 포이즌 메시지를 저장하는 데 사용할 수 있는 포이즌 하위 큐 기능을 제공합니다. 이 샘플에서는 Windows Vista를 사용하여 포이즌 메시지를 처리하는 모범 사례를 보여 줍니다.

Windows Vista에서 포이즌 메시지 검색은 정교합니다. 검색에 도움이 되는 3개의 속성이 있습니다. ReceiveRetryCount 지정된 메시지가 큐에서 다시 읽고 처리를 위해 애플리케이션으로 디스패치되는 횟수입니다. 메시지를 애플리케이션에 디스패치할 수 없거나 애플리케이션이 서비스 작업에서 트랜잭션을 롤백하기 때문에 큐에 다시 넣을 때 큐에서 메시지를 다시 읽습니다. MaxRetryCycles 는 메시지가 재시도 큐로 이동된 횟수입니다. 도달하면 ReceiveRetryCount 메시지가 다시 시도 큐로 이동됩니다. 이 속성 RetryCycleDelay 은 메시지가 다시 시도 큐에서 주 큐로 다시 이동하는 시간 지연입니다. 이 ReceiveRetryCount 값은 0으로 다시 설정됩니다. 메시지가 다시 시도됩니다. 메시지를 읽는 모든 시도가 실패한 경우 메시지는 포이즌으로 표시됩니다.

메시지가 유해로 표시되면 ReceiveErrorHandling 설정에 따라 메시지가 처리됩니다. 가능한 값을 반복하려면 다음을 수행합니다.

  • 오류(기본값): 수신기와 서비스 호스트에 오류를 지적합니다.

  • Drop: 메시지를 삭제합니다.

  • 메시지를 포이즌 메시지 하위 큐로 이동합니다. 이 값은 Windows Vista에서만 사용할 수 있습니다.

  • 거부: 메시지를 거부할 경우, 메시지를 보낸 사람의 비활성 메시지 큐로 다시 보냅니다. 이 값은 Windows Vista에서만 사용할 수 있습니다.

이 샘플에서는 독성 메시지에 대한 Move 배치를 사용하는 방법을 보여 줍니다. Move 는 메시지가 포이즌 하위 큐로 이동하도록 합니다.

서비스 계약은 IOrderProcessor큐에 사용하기에 적합한 단방향 서비스를 정의하는 계약입니다.

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface IOrderProcessor
{
    [OperationContract(IsOneWay = true)]
    void SubmitPurchaseOrder(PurchaseOrder po);
}

서비스 작업에는 주문을 처리 중이라는 메시지가 표시됩니다. SubmitPurchaseOrder 서비스 작업이 임의 호출 시 포이즌 메시지 기능을 보여주기 위해 예외를 던져 트랜잭션을 롤백합니다. 이렇게 하면 메시지가 큐에 다시 배치됩니다. 결국 메시지는 포이즌으로 표시됩니다. 포이즌 메시지를 포이즌 하위 큐로 이동하도록 구성이 설정됩니다.

// Service class that implements the service contract.
// Added code to write output to the console window.
public class OrderProcessorService : IOrderProcessor
{
    static Random r = new Random(137);

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void SubmitPurchaseOrder(PurchaseOrder po)
    {

        int randomNumber = r.Next(10);

        if (randomNumber % 2 == 0)
        {
            Orders.Add(po);
            Console.WriteLine("Processing {0} ", po);
        }
        else
        {
            Console.WriteLine("Aborting transaction, cannot process purchase order: " + po.PONumber);
            Console.WriteLine();
            throw new Exception("Cannot process purchase order: " + po.PONumber);
        }
    }

    public static void OnServiceFaulted(object sender, EventArgs e)
    {
        Console.WriteLine("Service Faulted");
    }

    // Host the service within this EXE console application.
    public static void Main()
    {
        // Get MSMQ queue name from app settings in configuration.
        string queueName = ConfigurationManager.AppSettings["queueName"];

        // Create the transacted MSMQ queue if necessary.
        if (!System.Messaging.MessageQueue.Exists(queueName))
            System.Messaging.MessageQueue.Create(queueName, true);

        // Get the base address that is used to listen for WS-MetaDataExchange requests.
        // This is useful to generate a proxy for the client.
        string baseAddress = ConfigurationManager.AppSettings["baseAddress"];

        // Create a ServiceHost for the OrderProcessorService type.
        ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService), new Uri(baseAddress));

        // Hook on to the service host faulted events.
        serviceHost.Faulted += new EventHandler(OnServiceFaulted);

        // Open the ServiceHostBase to create listeners and start listening for messages.
        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        if(serviceHost.State != CommunicationState.Faulted) {
            serviceHost.Close();
        }

    }
}

서비스 구성에는 다음 구성 파일에 표시된 대로, 포이즌 메시지 속성 receiveRetryCount, maxRetryCycles, retryCycleDelay, 및 receiveErrorHandling이(가) 포함됩니다.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!-- Use appSetting to configure MSMQ queue name. -->
    <add key="queueName" value=".\private$\ServiceModelSamplesPoison" />
    <add key="baseAddress" value="http://localhost:8000/orderProcessor/poisonSample"/>
  </appSettings>
  <system.serviceModel>
    <services>
      <service
              name="Microsoft.ServiceModel.Samples.OrderProcessorService">
        <!-- Define NetMsmqEndpoint -->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesPoison"
                  binding="netMsmqBinding"
                  bindingConfiguration="PoisonBinding"
                  contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
      </service>
    </services>

    <bindings>
      <netMsmqBinding>
        <binding name="PoisonBinding"
                 receiveRetryCount="0"
                 maxRetryCycles="1"
                 retryCycleDelay="00:00:05"
                 receiveErrorHandling="Move">
        </binding>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>
</configuration>

유해 메시지 큐에서 메시지 처리

포이즌 메시지 서비스는 최종 포이즌 메시지 큐에서 메시지를 읽고 처리합니다.

포이즌 메시지 큐의 메시지는 메시지를 처리하는 서비스로 주소가 지정된 메시지로, 포이즌 메시지 서비스 엔드포인트와 다를 수 있습니다. 따라서 포이즌 메시지 서비스가 큐에서 메시지를 읽을 때 WCF 채널 계층은 엔드포인트에서 불일치를 발견하고 메시지를 디스패치하지 않습니다. 이 경우 메시지는 주문 처리 서비스로 처리되지만 포이즌 메시지 서비스에서 수신됩니다. 다른 엔드포인트에 주소가 지정된 메시지도 계속 수신하려면, 메시지가 주소를 지정하는 모든 서비스 엔드포인트와 일치하도록 하는 필터링 기준에 ServiceBehavior를 추가해야 합니다. 포이즌 메시지 큐에서 읽은 메시지를 성공적으로 처리하는 데 필요합니다.

포이즌 메시지 서비스 구현 자체는 서비스 구현과 매우 유사합니다. 계약을 구현하고 주문을 처리합니다. 코드 예제는 다음과 같습니다.

// Service class that implements the service contract.
// Added code to write output to the console window.
[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]
public class OrderProcessorService : IOrderProcessor
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void SubmitPurchaseOrder(PurchaseOrder po)
    {
        Orders.Add(po);
        Console.WriteLine("Processing {0} ", po);
    }

    public static void OnServiceFaulted(object sender, EventArgs e)
    {
        Console.WriteLine("Service Faulted...exiting app");
        Environment.Exit(1);
    }

    // Host the service within this EXE console application.
    public static void Main()
    {

        // Create a ServiceHost for the OrderProcessorService type.
        ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService));

        // Hook on to the service host faulted events.
        serviceHost.Faulted += new EventHandler(OnServiceFaulted);

        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The poison message service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        // Close the ServiceHostBase to shutdown the service.
        if(serviceHost.State != CommunicationState.Faulted)
        {
            serviceHost.Close();
        }
    }

주문 큐에서 메시지를 읽는 주문 처리 서비스와 달리 포이즌 메시지 서비스는 포이즌 하위 큐에서 메시지를 읽습니다. 포이즌 큐는 주 큐의 하위 큐이며 이름이 "포이즌"이며 MSMQ에서 자동으로 생성됩니다. 액세스하려면 다음 샘플 구성과 같이 주 큐 이름 뒤에 ";" 및 하위 큐 이름(이 경우 -"poison")을 제공합니다.

비고

MSMQ v3.0 샘플에서 포이즌 큐 이름은 하위 큐가 아니라 메시지를 이동한 큐입니다.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Microsoft.ServiceModel.Samples.OrderProcessorService">
        <!-- Define NetMsmqEndpoint -->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesPoison;poison"
                  binding="netMsmqBinding"
                  contract="Microsoft.ServiceModel.Samples.IOrderProcessor" >
        </endpoint>
      </service>
    </services>

  </system.serviceModel>
</configuration>

샘플을 실행하면 클라이언트, 서비스 및 포이즌 메시지 서비스 활동이 콘솔 창에 표시됩니다. 서비스가 클라이언트에서 메시지를 수신하는 것을 볼 수 있습니다. 각 콘솔 창에서 Enter 키를 눌러 서비스를 종료합니다.

서비스는 실행, 주문 처리 및 임의로 처리를 종료하기 시작합니다. 메시지가 주문을 처리했음을 나타내는 경우 서비스가 실제로 메시지를 종료한 것을 볼 때까지 클라이언트를 다시 실행하여 다른 메시지를 보낼 수 있습니다. 구성된 포이즌 설정에 따라 메시지는 최종 포이즌 큐로 이동하기 전에 처리를 위해 한 번 시도됩니다.

The service is ready.
Press <ENTER> to terminate service.

Processing Purchase Order: 0f063b71-93e0-42a1-aa3b-bca6c7a89546
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Pending

Processing Purchase Order: 5ef9a4fa-5a30-4175-b455-2fb1396095fa
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Pending

Aborting transaction, cannot process purchase order: 23e0b991-fbf9-4438-a0e2-20adf93a4f89

포이즌 메시지 서비스를 시작하여 포이즌 큐에서 포이즌 메시지를 읽습니다. 이 예제에서 포이즌 메시지 서비스는 메시지를 읽고 처리합니다. 종료되고 손상된 구매 주문이 포이즌 메시지 서비스에 의해 읽혀지는 것을 볼 수 있습니다.

The service is ready.
Press <ENTER> to terminate service.

Processing Purchase Order: 23e0b991-fbf9-4438-a0e2-20adf93a4f89
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Pending

샘플을 설정, 빌드 및 실행하려면

  1. Windows Communication Foundation 샘플 에 대한One-Time 설정 절차를 수행했는지 확인합니다.

  2. 서비스가 먼저 실행되면 큐가 있는지 확인합니다. 큐가 존재하지 않으면 서비스가 큐를 생성합니다. 서비스를 먼저 실행하여 큐를 만들거나 MSMQ 큐 관리자를 통해 만들 수 있습니다. 다음 단계에 따라 Windows 2008에서 큐를 만듭니다.

    1. Visual Studio 2012에서 서버 관리자를 엽니다.

    2. 기능 탭을 확장합니다.

    3. 프라이빗 메시지 큐 마우스 오른쪽 단추로 클릭하고 새 프라이빗 큐선택합니다.

    4. 트랜잭션 체크박스를 선택하세요.

    5. 새 큐의 이름으로 ServiceModelSamplesTransacted 입력합니다.

  3. 솔루션의 C# 또는 Visual Basic .NET 버전을 빌드하려면 Windows Communication Foundation 샘플빌드의 지침을 따릅니다.

  4. 단일 또는 컴퓨터 간 구성에서 샘플을 실행하려면 localhost 대신 실제 호스트 이름을 반영하도록 큐 이름을 변경하고 Windows Communication Foundation 샘플 실행의 지침을 따릅니다.

기본적으로 바인딩 전송에서는 netMsmqBinding 보안을 사용하도록 설정됩니다. 두 속성 MsmqAuthenticationModeMsmqProtectionLevel함께 전송 보안 유형을 결정합니다. 기본적으로 인증 모드는 로 Windows 설정되고 보호 수준은 .로 Sign설정됩니다. MSMQ가 인증 및 서명 기능을 제공하려면 도메인의 일부여야 합니다. 도메인에 속하지 않은 컴퓨터에서 이 샘플을 실행하면 "사용자의 내부 메시지 큐 인증서가 없습니다."라는 오류가 표시됩니다.

작업 그룹에 조인된 컴퓨터에서 샘플을 실행하려면

  1. 컴퓨터가 도메인에 속하지 않는 경우 다음 샘플 구성과 같이 인증 모드 및 보호 수준을 설정하여 None 전송 보안을 해제합니다.

    <bindings>
        <netMsmqBinding>
            <binding name="TransactedBinding">
                <security mode="None"/>
            </binding>
        </netMsmqBinding>
    </bindings>
    

    엔드포인트의 bindingConfiguration 특성을 설정하여 엔드포인트가 바인딩과 연결되어 있는지 확인합니다.

  2. 샘플을 실행하기 전에 PoisonMessageServer, 서버 및 클라이언트의 구성을 변경해야 합니다.

    비고

    security modeNone로 설정하는 것은 MsmqAuthenticationMode, MsmqProtectionLevel, 및 Message의 보안을 None로 설정하는 것과 같습니다.

  3. 메타 데이터 교환이 작동하려면 HTTP 바인딩을 사용하여 URL을 등록합니다. 이렇게 하려면 서비스가 관리자 권한 명령 창에서 실행되어야 합니다. 그렇지 않으면 다음과 Unhandled Exception: System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL http://+:8000/ServiceModelSamples/service/. Your process does not have access rights to this namespace (see https://go.microsoft.com/fwlink/?LinkId=70353 for details). ---> System.Net.HttpListenerException: Access is denied같은 예외가 발생합니다.