Bagikan melalui


Penanganan Pesan Racun di MSMQ 4.0

Sampel MSMQ4 menunjukkan cara melakukan penanganan pesan racun dalam layanan. Sampel ini didasarkan pada sampel Pengikatan MSMQ yang Ditransaksikan. Sampel ini menggunakan netMsmqBinding. Layanan ini adalah aplikasi konsol yang dihosting sendiri untuk memungkinkan Anda mengamati layanan yang menerima pesan antrean.

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.

Pesan racun adalah pesan yang berulang kali dibaca dari antrean saat layanan yang membaca pesan tidak dapat memproses pesan dan oleh karena itu mengakhiri transaksi yang mana pesan dibaca. Dalam kasus seperti itu, pesan dicoba lagi. Ini secara teoritis dapat berlangsung selamanya jika ada masalah dengan pesan. Ini hanya dapat terjadi saat Anda menggunakan transaksi untuk membaca dari antrean dan memanggil operasi layanan.

Berdasarkan versi MSMQ, NetMsmqBinding mendukung deteksi terbatas untuk deteksi penuh pesan racun. Setelah terdeteksi sebagai diracuni, pesan dapat ditangani dengan beberapa cara. Sekali lagi, berdasarkan versi MSMQ, NetMsmqBinding mendukung penanganan terbatas untuk penanganan penuh pesan racun.

Sampel ini menggambarkan fasilitas racun terbatas yang disediakan pada platform Windows Server 2003 dan Windows XP dan fasilitas racun lengkap yang disediakan di Windows Vista. Dalam kedua sampel, tujuannya adalah untuk memindahkan pesan racun keluar dari antrean ke antrean lain. Antrean itu kemudian dapat disajikan oleh layanan pesan racun.

Sampel Penanganan Racun MSMQ v4.0

Di Windows Vista, MSMQ menyediakan fasilitas subantrean racun yang dapat digunakan untuk menyimpan pesan racun. Sampel ini menunjukkan praktik terbaik untuk menangani pesan racun menggunakan Windows Vista.

Deteksi pesan racun di Windows Vista sangat canggih. Ada 3 properti yang membantu deteksi. ReceiveRetryCount adalah jumlah berapa kali pesan yang diberikan dibaca ulang dari antrean dan dikirim ke aplikasi untuk diproses. Pesan dibaca ulang dari antrean saat dimasukkan kembali ke dalam antrean karena pesan tidak dapat dikirim ke aplikasi atau aplikasi mengembalikan transaksi dalam operasi layanan. MaxRetryCycles adalah jumlah berapa kali pesan dipindahkan ke antrean coba lagi. Saat ReceiveRetryCount tercapai, pesan dipindahkan ke antrean coba lagi. RetryCycleDelay properti adalah penundaan waktu setelah pesan dipindahkan dari antrean coba lagi kembali ke antrean utama. ReceiveRetryCount diatur ulang ke 0. Pesan dicoba lagi. Jika semua upaya untuk membaca pesan telah gagal, pesan ditandai sebagai diracuni.

Setelah pesan ditandai sebagai diracuni, pesan ditangani sesuai dengan pengaturan dalam enumerasi ReceiveErrorHandling. Untuk menegaskan kembali kemungkinan nilai:

  • Kesalahan (default): Untuk menyalahkan pendengar dan juga host layanan.

  • Hilangkan: Untuk menghapus pesan.

  • Pindahkan: Untuk memindahkan pesan ke subantrean pesan racun. Nilai ini hanya tersedia di Windows Vista.

  • Tolak: Untuk menolak pesan, mengirim pesan kembali ke antrean surat mati pengirim. Nilai ini hanya tersedia di Windows Vista.

Sampel menunjukkan menggunakan disposisi Move untuk pesan racun. Move menyebabkan pesan pindah ke subantrean racun.

Kontrak layanan adalah IOrderProcessor, yang menentukan layanan satu arah yang cocok untuk digunakan dengan antrean.

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

Operasi layanan menampilkan pesan yang menyatakan sedang memproses pesanan. Untuk menunjukkan fungsionalitas pesan racun, operasi layanan SubmitPurchaseOrder menerapkan pengecualian untuk mengembalikan transaksi pada pemanggilan layanan secara acak. Ini menyebabkan pesan diletakkan kembali dalam antrean. Akhirnya, pesan ditandai sebagai racun. Konfigurasi diatur untuk memindahkan pesan racun ke subantrean racun.

// 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();
        }

    }
}

Konfigurasi layanan mencakup properti pesan racun berikut: receiveRetryCount, maxRetryCycles, retryCycleDelay, dan receiveErrorHandling seperti yang ditunjukkan dalam file konfigurasi berikut.

<?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>

Memproses pesan dari antrean pesan racun

Layanan pesan racun membaca pesan dari antrean pesan racun akhir dan memprosesnya.

Pesan dalam antrean pesan racun adalah pesan yang ditujukan ke layanan yang memproses pesan, yang bisa berbeda dari titik akhir layanan pesan racun. Oleh karena itu, saat layanan pesan racun membaca pesan dari antrean, lapisan saluran WCF menemukan ketidakcocokan di titik akhir dan tidak mengirimkan pesan. Dalam hal ini, pesan ditujukan ke layanan pemrosesan pesanan tetapi diterima oleh layanan pesan racun. Untuk terus menerima pesan meskipun pesan ditujukan ke titik akhir yang berbeda, kita harus menambahkan ServiceBehavior untuk memfilter alamat tempat kriteria kecocokan cocok dengan titik akhir layanan mana pun yang ditujukan pesan. Ini diperlukan untuk berhasil memproses pesan yang Anda baca dari antrean pesan racun.

Implementasi layanan pesan racun itu sendiri sangat mirip dengan implementasi layanan. Ini mengimplementasikan kontrak dan memproses pesanan. Contoh kodenya adalah sebagai berikut.

// 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();
        }
    }

Tidak seperti layanan pemrosesan pesanan yang membaca pesanan dari antrean pesanan, layanan pesan racun membaca pesan dari subantrean racun. Antrean racun adalah subantrean dari antrean utama, diberi nama "racun" dan secara otomatis dihasilkan oleh MSMQ. Untuk mengaksesnya, berikan nama antrean utama diikuti dengan ";" dan nama subantrean, dalam hal ini -"racun", seperti yang ditunjukkan dalam konfigurasi sampel berikut.

Catatan

Dalam sampel untuk MSMQ v3.0, nama antrean racun bukan sub-antrean, melainkan antrean tempat kami memindahkan pesan.

<?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>

Saat Anda menjalankan sampel, aktivitas layanan pesan klien, layanan, dan racun ditampilkan di jendela konsol. Anda dapat melihat layanan menerima pesan dari klien. Tekan ENTER di setiap jendela konsol untuk mematikan layanan.

Layanan mulai berjalan, memproses pesanan dan pada awal acak untuk mengakhiri pemrosesan. Jika pesan menunjukkan pesan telah memproses pesanan, Anda dapat menjalankan klien lagi untuk mengirim pesan lain sampai melihat layanan benar-benar menghentikan pesan. Berdasarkan pengaturan racun yang dikonfigurasi, pesan dicoba sekali untuk diproses sebelum memindahkannya ke antrean racun akhir.

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

Mulai layanan pesan racun untuk membaca pesan beracun dari antrean racun. Dalam contoh ini, layanan pesan racun membaca pesan dan memprosesnya. Anda dapat melihat bahwa pesanan pembelian yang dihentikan dan diracuni dibaca oleh layanan pesan racun.

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

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 konfigurasi satu atau lintas komputer, ubah nama antrean agar mencerminkan nama host aktual alih-alih localhost dan ikuti instruksi dalam Menjalankan Sampel Windows Communication Foundation.

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".

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 pada PoisonMessageServer, server, dan klien sebelum Anda menjalankan sampel.

    Catatan

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

  3. Agar Meta Data Exchange berfungsi, kami mendaftarkan URL dengan pengikatan http. Ini mengharuskan layanan berjalan di jendela perintah yang ditingkatkan. Jika tidak, Anda mendapatkan pengecualian seperti: 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.