次の方法で共有


MSMQ 4.0 での有害メッセージ処理

MSMQ4 サンプルは、サービスで有害メッセージ処理を実行する方法を示しています。 このサンプルは、Transacted MSMQ バインディング サンプルに基づいています。 このサンプルでは、 netMsmqBindingを使用します。 このサービスは、キューに登録されたメッセージを受信しているサービスを監視できるようにするためのセルフホステッド コンソール アプリケーションです。

キュー通信では、クライアントはキューを使用してサービスと通信します。 より正確には、クライアントはメッセージをキューに送信します。 サービスはキューからメッセージを受信します。 そのため、サービスとクライアントは、キューを使用して通信するために同時に実行する必要はありません。

有害メッセージは、メッセージを読み取るサービスがメッセージを処理できないため、メッセージが読み取られたトランザクションを終了するときに、キューから繰り返し読み取られたメッセージです。 このような場合、メッセージは再試行されます。 これは、メッセージに問題がある場合、理論的には永遠に続く可能性があります。 これは、トランザクションを使用してキューから読み取り、サービス操作を呼び出す場合にのみ発生します。

NETMsmqBinding では、MSMQ のバージョンに基づいて、有害メッセージの完全検出に限定された検出がサポートされています。 メッセージが有害として検出された後、いくつかの方法で処理できます。 ここでも、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 列挙体の設定に従って処理されます。 可能な値を繰り返し示すには:

  • 障害 (既定): リスナーとサービス ホストをエラーにします。

  • ドロップ: メッセージを削除します。

  • 移動: 有害メッセージ サブキューにメッセージを移動します。 この値は、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();
        }

    }
}

サービス構成には、次の構成ファイルに示すように、 receiveRetryCountmaxRetryCyclesretryCycleDelay、および 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();
        }
    }

注文キューからメッセージを読み取る注文処理サービスとは異なり、有害メッセージ サービスは有害サブキューからメッセージを読み取ります。 有害キューはメイン キューのサブキューであり、"poison" という名前で、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 キーを押して、サービスをシャットダウンします。

サービスは実行を開始し、注文を処理し、ランダムに処理を終了します。 メッセージが注文を処理したことを示している場合は、サービスが実際にメッセージを終了するまで、クライアントを再度実行して別のメッセージを送信できます。 構成された有害設定に基づいて、メッセージは最終的な有害キューに移動する前に、処理のために 1 回試行されます。

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の 2 つのプロパティによって決まります。 既定では、認証モードは Windows に設定され、保護レベルは Sign に設定されます。 MSMQ で認証と署名機能を提供するには、ドメインの一部である必要があります。 ドメインに含まれていないコンピューターでこのサンプルを実行すると、"ユーザーの内部メッセージ キュー証明書が存在しません" というエラーが表示されます。

ワークグループに参加しているコンピューターでサンプルを実行するには

  1. コンピューターがドメインに属していない場合は、次のサンプル構成に示すように、認証モードと保護レベルを None に設定してトランスポート セキュリティをオフにします。

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

    エンドポイントの bindingConfiguration 属性を設定して、エンドポイントがバインドに関連付けられていることを確認します。

  2. サンプルを実行する前に、PoisonMessageServer、サーバー、およびクライアントの構成を必ず変更してください。

    security modeNone に設定することは、MsmqAuthenticationModeMsmqProtectionLevelMessageセキュリティをNoneに設定することと同じです。

  3. Meta Data Exchange が機能するためには、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