WCF MSMQ隊(duì)列基本概念簡(jiǎn)述
今天,我們將會(huì)在這篇文章中為大家詳細(xì)介紹一下關(guān)于WCF MSMQ隊(duì)列的一些基本特性。希望對(duì)于初學(xué)者來(lái)說(shuō),可以從這里介紹的內(nèi)容中獲得一些幫助,并能夠充分掌握這些基本技巧,以方便我們的實(shí)際應(yīng)用。
WCF MSMQ隊(duì)列中共有兩種類型,事務(wù)性隊(duì)列(transactional queue)會(huì)將消息持久(persiste)存儲(chǔ)到磁盤中,即便服務(wù)器當(dāng)機(jī)(shutdown)、重啟(reboot)或崩潰(crash),消息依然可以在系統(tǒng)恢復(fù)后被讀取。同時(shí),消息發(fā)布、獲取和刪除都在環(huán)境事務(wù)范圍內(nèi),從而確保消息的可靠性。我們還可以使用 TransactionScope 將環(huán)境事務(wù)傳遞給隊(duì)列,否則隊(duì)列會(huì)自動(dòng)創(chuàng)建一個(gè)內(nèi)部事務(wù)。非事務(wù)性隊(duì)列(nontransactional volatile queues)只是將消息存在內(nèi)存,不會(huì)使用磁盤進(jìn)行持久存儲(chǔ),且不會(huì)使用事務(wù)來(lái)保護(hù)對(duì)消息的操作。一但服務(wù)器發(fā)生問(wèn)題,或者調(diào)用方出現(xiàn)異常,消息都會(huì)丟失。
- // 創(chuàng)建事務(wù)性隊(duì)列
- MessageQueue.Create(@".\private$\myqueue", true);
- // 創(chuàng)建非事務(wù)性隊(duì)列
- MessageQueue.Create(@".\private$\myqueue");
通過(guò)下面的例子我們會(huì)看到事務(wù)失敗時(shí),沒(méi)有任何消息被寫入隊(duì)列。
- [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();
- });
- }
- }
這里需要對(duì) "消息" 做一個(gè)澄清,當(dāng)客戶端發(fā)出調(diào)用(call)時(shí),調(diào)用會(huì)被轉(zhuǎn)換成 WCF Message,然后被包裝到 MSMQ Message 中。如果客戶端事務(wù)完成提交,那么 MSMQ Message 會(huì)被傳遞到隊(duì)列并存儲(chǔ)起來(lái)。相反,如果事務(wù)失敗,消息會(huì)被丟棄。上面的例子中,我們將多個(gè)調(diào)用放到一個(gè)環(huán)境事務(wù)中,也可以將多個(gè)服務(wù)調(diào)用放到一個(gè)事務(wù)當(dāng)中。如果隊(duì)列服務(wù)不在當(dāng)前機(jī)器上,也就是說(shuō)使用 Public Queue 時(shí),客戶端的消息隊(duì)列組件將承擔(dān) "代理(proxy)" 的角色。客戶端的調(diào)用會(huì)首先存儲(chǔ)到本地隊(duì)列,然后再由本地隊(duì)列轉(zhuǎn)發(fā)給目標(biāo)隊(duì)列。這個(gè)轉(zhuǎn)發(fā)過(guò)程同樣受到事務(wù)保護(hù)。
要是開發(fā)非事務(wù)性消息隊(duì)列服務(wù),需要用到 NetMsmqBinding 的兩個(gè)屬性。將 Durable 設(shè)為 false,表示不使用事務(wù)方式訪問(wèn)消息隊(duì)列。另外還得將 ExactlyOnce 設(shè)為 false,否則會(huì)拋出 InvalidOperationException 異常。
下面例子中,重啟消息隊(duì)列服務(wù)(Message Queuing)后,你會(huì)發(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服務(wù),然后按任意鍵繼續(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();
- });
- }
- }
【編輯推薦】