WCF MSMQ隊列基本概念簡述
今天,我們將會在這篇文章中為大家詳細介紹一下關于WCF MSMQ隊列的一些基本特性。希望對于初學者來說,可以從這里介紹的內容中獲得一些幫助,并能夠充分掌握這些基本技巧,以方便我們的實際應用。
WCF MSMQ隊列中共有兩種類型,事務性隊列(transactional queue)會將消息持久(persiste)存儲到磁盤中,即便服務器當機(shutdown)、重啟(reboot)或崩潰(crash),消息依然可以在系統(tǒng)恢復后被讀取。同時,消息發(fā)布、獲取和刪除都在環(huán)境事務范圍內,從而確保消息的可靠性。我們還可以使用 TransactionScope 將環(huán)境事務傳遞給隊列,否則隊列會自動創(chuàng)建一個內部事務。非事務性隊列(nontransactional volatile queues)只是將消息存在內存,不會使用磁盤進行持久存儲,且不會使用事務來保護對消息的操作。一但服務器發(fā)生問題,或者調用方出現(xiàn)異常,消息都會丟失。
- // 創(chuàng)建事務性隊列
- MessageQueue.Create(@".\private$\myqueue", true);
- // 創(chuàng)建非事務性隊列
- MessageQueue.Create(@".\private$\myqueue");
通過下面的例子我們會看到事務失敗時,沒有任何消息被寫入隊列。
- [ServiceContract]
- public interface IService
- {
- [OperationContract(IsOneWay = true)]
- void Test(int i);
- }
- [ServiceBehavior]
- public class MyService : IService
- {
- [OperationBehavior]
- public void Test(int i)
- {
- Console.WriteLine(i);
- }
- }
- public class WcfTest
- {
- public static void Test()
- {
- if (!MessageQueue.Exists(@".\private$\myqueue"))
- {
- MessageQueue.Create(@".\private$\myqueue", true);
- }
- IService client = ChannelFactory<IService>.CreateChannel(
- new NetMsmqBinding(NetMsmqSecurityMode.None),
- new EndpointAddress("net.msmq://localhost/private/myqueue"));
- try
- {
- using (TransactionScope scope = new TransactionScope())
- {
- using (client as IDisposable)
- {
- for (int i = 0; i < 10; i++)
- {
- client.Test(i);
- if (i > 5) throw new Exception();
- }
- }
- scope.Complete();
- }
- }
- catch
- {
- }
- AppDomain.CreateDomain("Server").DoCallBack(delegate
- {
- ServiceHost host = new ServiceHost(typeof(MyService),
new Uri("net.msmq://localhost/private/myqueue"));- host.AddServiceEndpoint(typeof(IService),
new NetMsmqBinding(NetMsmqSecurityMode.None), "");- host.Open();
- });
- }
- }
這里需要對 "消息" 做一個澄清,當客戶端發(fā)出調用(call)時,調用會被轉換成 WCF Message,然后被包裝到 MSMQ Message 中。如果客戶端事務完成提交,那么 MSMQ Message 會被傳遞到隊列并存儲起來。相反,如果事務失敗,消息會被丟棄。上面的例子中,我們將多個調用放到一個環(huán)境事務中,也可以將多個服務調用放到一個事務當中。如果隊列服務不在當前機器上,也就是說使用 Public Queue 時,客戶端的消息隊列組件將承擔 "代理(proxy)" 的角色??蛻舳说恼{用會首先存儲到本地隊列,然后再由本地隊列轉發(fā)給目標隊列。這個轉發(fā)過程同樣受到事務保護。
要是開發(fā)非事務性消息隊列服務,需要用到 NetMsmqBinding 的兩個屬性。將 Durable 設為 false,表示不使用事務方式訪問消息隊列。另外還得將 ExactlyOnce 設為 false,否則會拋出 InvalidOperationException 異常。
下面例子中,重啟消息隊列服務(Message Queuing)后,你會發(fā)現(xiàn)消息丟失。
- [ServiceContract]
- public interface IService
- {
- [OperationContract(IsOneWay = true)]
- void Test(int i);
- }
- [ServiceBehavior]
- public class MyService : IService
- {
- public MyService()
- {
- Console.WriteLine("Constructor...");
- }
- [OperationBehavior(TransactionScopeRequired=true)]
- public void Test(int i)
- {
- Console.WriteLine(i);
- }
- }
- public class WcfTest
- {
- public static void Test()
- {
- MessageQueue.Delete(@".\private$\myqueue");
- MessageQueue.Create(@".\private$\myqueue");
- NetMsmqBinding binding1 = new NetMsmqBinding
(NetMsmqSecurityMode.None);- binding1.Durable = false;
- binding1.ExactlyOnce = false;
- IService client = ChannelFactory<IService>
.CreateChannel(binding1,- new EndpointAddress("net.msmq://localhost/private/myqueue"));
- using (client as IDisposable)
- {
- for (int i = 0; i < 10; i++)
- {
- client.Test(i);
- }
- }
- Console.WriteLine("重啟MSMQ服務,然后按任意鍵繼續(xù)...");
- Console.ReadKey(true);
- AppDomain.CreateDomain("Server").DoCallBack(delegate
- {
- NetMsmqBinding binding2 = new NetMsmqBinding
(NetMsmqSecurityMode.None);- binding2.Durable = false;
- binding2.ExactlyOnce = false;
- ServiceHost host = new ServiceHost(typeof(MyService),
new Uri("net.msmq://localhost/private/myqueue"));- host.AddServiceEndpoint(typeof(IService), binding2, "");
- host.Open();
- });
- }
- }
【編輯推薦】