今天,我们将会在这篇文章中为大家详细介绍一下关于WCF MSMQ队列的一些基本特性。希望对于初学者来说,可以从这里介绍的内容中获得一些帮助,并能够充分掌握这些基本技巧,以方便我们的实际应用。
WCF MSMQ队列中共有两种类型,事务性队列(transactional queue)会将消息持久(persiste)存储到磁盘中,即便服务器当机(shutdown)、重启(reboot)或崩溃(crash),消息依然可以在系统恢复后被读取。同时,消息发布、获取和删除都在环境事务范围内,从而确保消息的可靠性。我们还可以使用 TransactionScope 将环境事务传递给队列,否则队列会自动创建一个内部事务。非事务性队列(nontransactional volatile queues)只是将消息存在内存,不会使用磁盘进行持久存储,且不会使用事务来保护对消息的操作。一但服务器发生问题,或者调用方出现异常,消息都会丢失。
- // 创建事务性队列
- MessageQueue.Create(@".\private$\myqueue", true);
- // 创建非事务性队列
- 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();
- });
- }
- }
这里需要对 "消息" 做一个澄清,当客户端发出调用(call)时,调用会被转换成 WCF Message,然后被包装到 MSMQ Message 中。如果客户端事务完成提交,那么 MSMQ Message 会被传递到队列并存储起来。相反,如果事务失败,消息会被丢弃。上面的例子中,我们将多个调用放到一个环境事务中,也可以将多个服务调用放到一个事务当中。如果队列服务不在当前机器上,也就是说使用 Public Queue 时,客户端的消息队列组件将承担 "代理(proxy)" 的角色。客户端的调用会首先存储到本地队列,然后再由本地队列转发给目标队列。这个转发过程同样受到事务保护。
要是开发非事务性消息队列服务,需要用到 NetMsmqBinding 的两个属性。将 Durable 设为 false,表示不使用事务方式访问消息队列。另外还得将 ExactlyOnce 设为 false,否则会抛出 InvalidOperationException 异常。
下面例子中,重启消息队列服务(Message Queuing)后,你会发现消息丢失。
- [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服务,然后按任意键继续...");
- 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();
- });
- }
- }
【编辑推荐】