共用方式為


MSMQ 4.0 中的有害訊息處理

MSMQ4 範例示範如何在服務中執行有害訊息處理。 此範例是以 Transacted 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();
        }

    }
}

服務組態包含下列有害訊息屬性: receiveRetryCountmaxRetryCyclesretryCycleDelayreceiveErrorHandling ,如下列組態檔所示。

<?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 相當於將 MsmqAuthenticationModeMsmqProtectionLevelMessage 的安全性設定為 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