Bagikan melalui


Antrean Surat Gagal

Sampel DeadLetter menunjukkan cara menangani dan memproses pesan yang gagal dikirim. Hal ini didasarkan pada sampel Pengikatan MSMQ yang Ditransaksikan. Sampel ini menggunakan pengikatan netMsmqBinding. Layanan ini adalah aplikasi konsol yang dihosting sendiri untuk memungkinkan Anda mengamati layanan yang menerima pesan antrean.

Catatan

Prosedur penyiapan dan petunjuk pembuatan untuk sampel ini terdapat di akhir topik ini.

Catatan

Sampel ini menunjukkan setiap antrean surat mati aplikasi yang hanya tersedia di Windows Vista. Sampel dapat dimodifikasi untuk menggunakan antrean default di seluruh sistem untuk MSMQ 3.0 pada Windows Server 2003 dan Windows XP.

Di komunikasi dalam antrean, klien berkomunikasi ke layanan menggunakan antrean. Lebih tepatnya, klien mengirim pesan ke antrean. Layanan menerima pesan dari antrean. Oleh karena itu, layanan dan klien tidak harus berjalan pada saat yang bersamaan untuk berkomunikasi menggunakan antrean.

Karena komunikasi antrean dapat melibatkan sejumlah dormansi, Anda mungkin ingin mengaitkan nilai waktu aktif pada pesan untuk memastikan bahwa pesan tidak terkirim ke aplikasi jika telah melewati waktu. Ada juga kasus, saat aplikasi harus diberi tahu apakah pesan gagal dikirim. Dalam semua kasus ini, seperti ketika waktu aktif pada pesan telah kedaluwarsa atau pesan gagal dikirim, pesan dimasukkan ke dalam antrean surat mati. Aplikasi pengiriman kemudian dapat membaca pesan dalam antrean surat mati dan mengambil tindakan korektif yang berkisar dari tidak ada tindakan untuk memperbaiki alasan pengiriman yang gagal dan mengirim ulang pesan.

Antrean surat mati dalam pengikatan NetMsmqBinding dinyatakan dalam properti berikut:

  • Properti DeadLetterQueue untuk menyatakan jenis antrean surat mati yang diperlukan oleh klien. Enumerasi ini memiliki nilai berikut:

  • None: Tidak ada antrean surat mati yang diperlukan oleh klien.

  • System: Antrean surat mati sistem digunakan untuk menyimpan pesan mati. Antrean surat mati sistem dibagikan oleh semua aplikasi yang berjalan di komputer.

  • Custom: Antrean surat mati kustom yang ditentukan menggunakan properti CustomDeadLetterQueue digunakan untuk menyimpan pesan mati. Fitur ini hanya tersedia di Windows Vista. Fitur ini digunakan ketika aplikasi harus menggunakan antrean surat mati sendiri alih-alih membagikannya dengan aplikasi lain yang berjalan di komputer yang sama.

  • Properti CustomDeadLetterQueue untuk menyatakan antrean tertentu untuk digunakan sebagai antrean surat mati. Properti ini hanya tersedia di Windows Vista.

Dalam sampel ini, klien mengirim batch pesan ke layanan dari dalam cakupan transaksi dan menentukan nilai yang sangat rendah untuk "waktu aktif" untuk pesan ini (sekitar 2 detik). Klien juga menentukan antrean surat mati kustom yang akan digunakan untuk mengantrekan pesan yang telah kedaluwarsa.

Aplikasi klien dapat membaca pesan dalam antrean surat mati dan mencoba kembali pengiriman pesan atau memperbaiki kesalahan yang menyebabkan pesan asli diletakkan dalam antrean surat mati dan mengirim pesan. Dalam sampel, klien menampilkan pesan kesalahan.

Kontrak layanan adalah IOrderProcessor, seperti yang ditunjukkan dalam kode sampel berikut.

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

Kode layanan dalam sampel adalah dari Pengikatan MSMQ Yang Ditransaksikan.

Komunikasi dengan layanan terjadi dalam cakupan transaksi. Layanan membaca pesan dari antrean, melakukan operasi dan kemudian menampilkan hasil operasi. Aplikasi ini juga membuat antrean surat mati untuk pesan surat mati.

//The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.

//Client implementation code.
class Client
{
    static void Main()
    {
        // Get MSMQ queue name from app settings in configuration
        string deadLetterQueueName = ConfigurationManager.AppSettings["deadLetterQueueName"];

        // Create the transacted MSMQ queue for storing dead message if necessary.
        if (!MessageQueue.Exists(deadLetterQueueName))
            MessageQueue.Create(deadLetterQueueName, true);

        // Create a proxy with given client endpoint configuration
        OrderProcessorClient client = new OrderProcessorClient("OrderProcessorEndpoint");

        // Create the purchase order
        PurchaseOrder po = new PurchaseOrder();
        po.CustomerId = "somecustomer.com";
        po.PONumber = Guid.NewGuid().ToString();

        PurchaseOrderLineItem lineItem1 = new PurchaseOrderLineItem();
        lineItem1.ProductId = "Blue Widget";
        lineItem1.Quantity = 54;
        lineItem1.UnitCost = 29.99F;

        PurchaseOrderLineItem lineItem2 = new PurchaseOrderLineItem();
        lineItem2.ProductId = "Red Widget";
        lineItem2.Quantity = 890;
        lineItem2.UnitCost = 45.89F;

        po.orderLineItems = new PurchaseOrderLineItem[2];
        po.orderLineItems[0] = lineItem1;
        po.orderLineItems[1] = lineItem2;

        //Create a transaction scope.
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
        {
            // Make a queued call to submit the purchase order
            client.SubmitPurchaseOrder(po);
            // Complete the transaction.
            scope.Complete();
        }

        client.Close();

        Console.WriteLine();
        Console.WriteLine("Press <ENTER> to terminate client.");
        Console.ReadLine();
    }
}

Konfigurasi klien menentukan durasi singkat untuk pesan untuk menjangkau layanan. Jika pesan tidak dapat ditransmisikan dalam durasi yang ditentukan, pesan akan kedaluwarsa dan dipindahkan ke antrean surat mati.

Catatan

Klien dapat mengirimkan pesan ke antrean layanan dalam waktu yang ditentukan. Untuk memastikan Anda melihat layanan surat mati dalam tindakan, Anda harus menjalankan klien sebelum memulai layanan. Waktu pesan habis dan dikirim ke layanan surat mati.

Aplikasi harus menentukan antrean mana yang akan digunakan sebagai antrean surat mati. Jika tidak ada antrean yang ditentukan, antrean surat mati transaksional default di seluruh sistem digunakan untuk mengantrekan pesan mati. Dalam contoh ini, aplikasi klien menentukan antrean surat mati aplikasinya sendiri.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!-- use appSetting to configure MSMQ Dead Letter queue name -->
    <add key="deadLetterQueueName" value=".\private$\ServiceModelSamplesOrdersAppDLQ"/>
  </appSettings>

  <system.serviceModel>
    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint name="OrderProcessorEndpoint"
                address="net.msmq://localhost/private/ServiceModelSamplesDeadLetter"
                binding="netMsmqBinding"
                bindingConfiguration="PerAppDLQBinding"
                contract="IOrderProcessor" />
    </client>

    <bindings>
      <netMsmqBinding>
        <binding name="PerAppDLQBinding"
                 deadLetterQueue="Custom"
                 customDeadLetterQueue="net.msmq://localhost/private/ServiceModelSamplesOrdersAppDLQ"
                 timeToLive="00:00:02"/>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>

</configuration>

Layanan pesan surat mati membaca pesan dari antrean surat mati. Layanan pesan surat mati menerapkan kontrak IOrderProcessor. Namun implementasinya bukan untuk memproses pesanan. Layanan pesan surat mati adalah layanan klien dan tidak memiliki fasilitas untuk memproses pesanan.

Catatan

Antrean surat mati adalah antrean klien dan bersifat lokal bagi pengelola antrean klien.

Implementasi layanan pesan surat mati memeriksa alasan pesan gagal dikirim dan mengambil tindakan korektif. Alasan kegagalan pesan ditangkap dalam dua enumerasi, DeliveryFailure dan DeliveryStatus. Anda dapat mengambil MsmqMessageProperty dari OperationContext seperti yang ditunjukkan dalam kode sampel berikut:

public void SubmitPurchaseOrder(PurchaseOrder po)
{
    Console.WriteLine("Submitting purchase order did not succeed ", po);
    MsmqMessageProperty mqProp =
                  OperationContext.Current.IncomingMessageProperties[
                  MsmqMessageProperty.Name] as MsmqMessageProperty;
    Console.WriteLine("Message Delivery Status: {0} ",
                                                mqProp.DeliveryStatus);
    Console.WriteLine("Message Delivery Failure: {0}",
                                               mqProp.DeliveryFailure);
    Console.WriteLine();
    …
}

Pesan dalam antrean surat mati adalah pesan yang ditujukan ke layanan yang memproses pesan. Oleh karena itu, ketika layanan pesan surat mati membaca pesan dari antrean, lapisan saluran Windows Communication Foundation (WCF) menemukan ketidakcocokan di titik akhir dan tidak mengirim pesan. Dalam hal ini, pesan ditujukan ke layanan pemrosesan pesanan tetapi diterima oleh layanan pesan surat mati. Untuk menerima pesan yang ditujukan ke titik akhir yang berbeda, filter alamat yang cocok dengan alamat apa pun ditentukan dalam ServiceBehavior. Hal ini diperlukan untuk berhasil memproses pesan yang dibaca dari antrean surat mati.

Dalam sampel ini, layanan pesan surat mati mengirim ulang pesan jika alasan kegagalan adalah bahwa waktu pesan tersebut habis. Untuk semua alasan lain, ini menampilkan kegagalan pengiriman, seperti yang ditunjukkan dalam kode sampel berikut:

// Service class that implements the service contract.
// Added code to write output to the console window.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Single, AddressFilterMode=AddressFilterMode.Any)]
public class PurchaseOrderDLQService : IOrderProcessor
{
    OrderProcessorClient orderProcessorService;
    public PurchaseOrderDLQService()
    {
        orderProcessorService = new OrderProcessorClient("OrderProcessorEndpoint");
    }

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void SubmitPurchaseOrder(PurchaseOrder po)
    {
        Console.WriteLine("Submitting purchase order did not succeed ", po);
        MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty;

        Console.WriteLine("Message Delivery Status: {0} ", mqProp.DeliveryStatus);
        Console.WriteLine("Message Delivery Failure: {0}", mqProp.DeliveryFailure);
        Console.WriteLine();

        // resend the message if timed out
        if (mqProp.DeliveryFailure == DeliveryFailure.ReachQueueTimeout ||
            mqProp.DeliveryFailure == DeliveryFailure.ReceiveTimeout)
        {
            // re-send
            Console.WriteLine("Purchase order Time To Live expired");
            Console.WriteLine("Trying to resend the message");

            // reuse the same transaction used to read the message from dlq to enqueue the message to app. queue
            orderProcessorService.SubmitPurchaseOrder(po);
            Console.WriteLine("Purchase order resent");
        }
    }

    // Host the service within this EXE console application.
    public static void Main()
    {
        // Create a ServiceHost for the PurchaseOrderDLQService type.
        using (ServiceHost serviceHost = new ServiceHost(typeof(PurchaseOrderDLQService)))
        {
            // Open the ServiceHostBase to create listeners and start listening for messages.
            serviceHost.Open();

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

Sampel berikut menunjukkan konfigurasi untuk pesan surat mati:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
          name="Microsoft.ServiceModel.Samples.PurchaseOrderDLQService">
        <!-- Define NetMsmqEndpoint in this case, DLQ end point to read messages-->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesOrdersAppDLQ"
                  binding="netMsmqBinding"
                  bindingConfiguration="DefaultBinding"
                  contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
      </service>
    </services>

    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint name="OrderProcessorEndpoint"
                 address="net.msmq://localhost/private/ServiceModelSamplesDeadLetter"
                 binding="netMsmqBinding"
                 bindingConfiguration="SystemDLQBinding"
                 contract="IOrderProcessor" />
    </client>

    <bindings>
      <netMsmqBinding>
        <binding name="DefaultBinding" />
        <binding name="SystemDLQBinding"
                 deadLetterQueue="System"/>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Dalam menjalankan sampel, ada 3 executable untuk dijalankan untuk melihat bagaimana antrean surat mati bekerja untuk setiap aplikasi; klien, layanan, dan layanan surat mati yang membaca dari antrean surat mati untuk setiap aplikasi dan mengirim ulang pesan ke layanan. Semua adalah aplikasi konsol dengan output di jendela konsol.

Catatan

Karena antrean sedang digunakan, klien dan layanan tidak harus aktif dan dijalankan secara bersamaan. Anda dapat menjalankan klien, mematikannya, dan kemudian memulai layanan dan klien masih akan menerima pesannya. Anda harus memulai layanan dan mematikannya sehingga antrean dapat dibuat.

Saat menjalankan klien, klien menampilkan pesan:

Press <ENTER> to terminate client.

Klien berusaha mengirim pesan tetapi dengan batas waktu yang singkat, pesan kedaluwarsa dan sekarang diantrekan dalam antrean surat mati untuk setiap aplikasi.

Anda kemudian menjalankan layanan surat mati, yang membaca pesan dan menampilkan kode kesalahan dan mengirim ulang pesan kembali ke layanan.

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

Submitting purchase order did not succeed
Message Delivery Status: InDoubt
Message Delivery Failure: ReachQueueTimeout

Purchase order Time To Live expired
Trying to resend the message
Purchase order resent

Layanan dimulai dan kemudian membaca pesan yang dikirim ulang dan memprosesnya.

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

Processing Purchase Order: 97897eff-f926-4057-a32b-af8fb11b9bf9
        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

Untuk menyiapkan, membangun, dan menjalankan sampel

  1. Pastikan Anda telah melakukan Prosedur Penyiapan Satu Kali untuk Sampel Windows Communication Foundation.

  2. Jika layanan dijalankan terlebih dahulu, layanan akan memeriksa untuk memastikan bahwa antrean ada. Jika antrean tidak ada, layanan akan membuatnya. Anda dapat menjalankan layanan terlebih dahulu untuk membuat antrean, atau Anda dapat membuatnya melalui Pengelola Antrean MSMQ. Ikuti langkah-langkah ini untuk membuat antrean di Windows 2008.

    1. Buka Pengelola Server di Visual Studio 2012.

    2. Luaskan tab Fitur.

    3. Klik kanan Antrean Pesan Privat, dan pilih Baru, Antrean Privat.

    4. Centang kotak Transaksional.

    5. Masukkan ServiceModelSamplesTransacted sebagai nama antrean baru.

  3. Untuk membangun solusi edisi C# atau Visual Basic .NET, ikuti petunjuknya di Membangun Sampel WCF.

  4. Untuk menjalankan sampel dalam nama antrean perubahan konfigurasi tunggal atau lintas komputer dengan tepat, ganti localhost dengan nama lengkap komputer dan ikuti instruksi dalam Menjalankan Sampel Windows Communication Foundation.

Untuk menjalankan sampel di komputer yang digabungkan ke grup kerja

  1. Jika komputer Anda bukan bagian dari domain, nonaktifkan keamanan transportasi dengan mengatur mode autentikasi dan tingkat perlindungan ke None seperti yang ditunjukkan dalam konfigurasi sampel berikut:

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

    Pastikan titik akhir dikaitkan dengan pengikatan dengan mengatur atribut bindingConfiguration titik akhir.

  2. Pastikan Anda mengubah konfigurasi di DeadLetterService, server, dan klien sebelum menjalankan sampel.

    Catatan

    Mengatur security mode ke None sama dengan mengatur keamanan MsmqAuthenticationMode, MsmqProtectionLevel dan Message ke None.

Komentar

Secara default dengan transportasi pengikatan netMsmqBinding, keamanan diaktifkan. Dua properti, MsmqAuthenticationMode dan MsmqProtectionLevel, bersama-sama menentukan jenis keamanan transportasi. Secara default, mode autentikasi diatur ke Windows dan tingkat perlindungan diatur ke Sign. Agar menyediakan fitur autentikasi dan penandatanganan, MSMQ harus menjadi bagian dari domain. Jika Anda menjalankan sampel ini di komputer yang bukan bagian dari domain, Anda menerima kesalahan berikut: "Sertifikat antrean pesan internal pengguna tidak ada".