消息队列不同于传统的请求响应模式,它是客户端把消息发送给请求消息队列,服务可以稍后对消息进行处理并把处理结果发送给响应队列,而后客户端从响应队列读取服务处理后的消息。而且使用消息队列可以使客户端实现脱机工作。脱机应用程序必须有本地缓存数据,要采用异步通讯而且要把消息持久化,在与服务器联机后将消息发送出去。WCF是使用NetMsmqBinding来支持消息队列的,传输消息不是通过TCP或HTTP等,而是通过微软消息队列(MSMQ),这是Windows组件,可以通过1)控制面板2)程序和功能:打开或关闭Windows功能3)出现如下界面,点确定即可安装。
安装成功后可以右击我的电脑选择管理而后会有如下界面:
理论也不说那么多了,MSDN上说的很详细,还是写些相对简单清晰的代码的整体上把握下消息队列,有时候看书不是太明白的地方,调试下代码或许会有种豁然开朗的感觉。因为要维护服务端和客户端双向通讯,所以需要两个队列,两个单向操作契约。而客户端的功能既要发送消息到请求消息队列又要从响应消息队列读取响应消息,服务端则需要从调用队列读取消息进行处理,处理后发送的响应消息队列。实际上客户端和服务端并没有直接通讯,而是通过两个队列来进行通讯的。
请求契约:
using System;
using System.ServiceModel;
namespace IFruit
{
[ServiceContract]
public interface IFruitService
{
[OperationContract(IsOneWay = true )]
void GetFruitInfo( string fruitName, string price);
}
}
请求服务实现:
using System;
using System.Messaging;
using System.ServiceModel;
using IFruit;
using IFruitResponse;
namespace FruitSvc
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class FruitService:IFruitService
{
[OperationBehavior(TransactionScopeRequired = true )]
public void GetFruitInfo( string fruitName, string price)
{
string info = string .Empty;
ExceptionDetail error = null ;
try
{
info = string .Format( " The Fruit Name Is {0} And Price Is {1} " , fruitName, price);
}
catch (Exception ex)
{
error = new ExceptionDetail(ex);
}
finally
{
// 创建队列
string queueName = " .\\private$\\FruitResponseQueue " ;
if ( ! MessageQueue.Exists(queueName))
{
MessageQueue.Create(queueName, true );
}
// 把处理后的消息放到响应消息队列
EndpointAddress address = new EndpointAddress( " net.msmq://localhost/private/FruitResponseQueue " );
NetMsmqBinding binding = new NetMsmqBinding();
binding.Security.Mode = NetMsmqSecurityMode.None;
using (ChannelFactory < IFruitResponseService > factory = new ChannelFactory < IFruitResponseService > (binding,
address))
{
IFruitResponseService response = factory.CreateChannel();
response.OnGetFruitInfoCompleted(info, error);
}
}
}
}
}
响应契约:
using System;
using System.ServiceModel;
namespace IFruitResponse
{
[ServiceContract]
public interface IFruitResponseService
{
[OperationContract(IsOneWay = true )]
void OnGetFruitInfoCompleted( string fruitInfo, ExceptionDetail error);
}
}
响应服务实现:
using System;
using System.Collections.Generic;
using System.ServiceModel;
using IFruitResponse;
namespace FruitResponse
{
// 定义一泛型委托
public delegate void GenericEventHandler < T > (T t);
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class FruitResponseService : IFruitResponseService
{
// 声明并初始化一委托事件
public static event GenericEventHandler < string > GetFruitInfoCompleted = delegate { };
[OperationBehavior(TransactionScopeRequired = true )]
public void OnGetFruitInfoCompleted( string fruitInfo, ExceptionDetail error)
{
if (error == null )
{
GetFruitInfoCompleted(fruitInfo);
}
}
}
}
客户端服务寄存:
using System;
using System.ServiceModel;
using System.Messaging;
using FruitResponse;
using IFruit;
namespace FruitClientHost
{
class Program
{
static void Main( string [] args)
{
// GetFruitInfoCompleted发生后调用OnGetFruitInfoCompleted方法
FruitResponseService.GetFruitInfoCompleted += Program.OnGetFruitInfoCompleted;
// 创建两个队列
string queueName = " .\\private$\\GetFruitInfoQueue " ;
if ( ! MessageQueue.Exists(queueName))
{
MessageQueue.Create(queueName, true );
}
string queueName1 = " .\\private$\\FruitResponseQueue " ;
if ( ! MessageQueue.Exists(queueName1))
{
MessageQueue.Create(queueName1, true );
}
// 接收响应消息
ServiceHost fruitHost = new ServiceHost( typeof (FruitResponseService),
new Uri( " net.msmq://localhost/private/FruitResponseQueue " ));
NetMsmqBinding netMsmqBind = new NetMsmqBinding();
netMsmqBind.Security.Mode = NetMsmqSecurityMode.None;
fruitHost.AddServiceEndpoint( typeof (IFruitResponse.IFruitResponseService), netMsmqBind, "" );
fruitHost.Open();
// 发送请求消息到请求队列
EndpointAddress address = new EndpointAddress( " net.msmq://localhost/private/GetFruitInfoQueue " );
NetMsmqBinding binding = new NetMsmqBinding();
binding.Security.Mode = NetMsmqSecurityMode.None;
using (ChannelFactory < IFruitService > factory = new ChannelFactory < IFruitService > (binding, address))
{
IFruitService fruit = factory.CreateChannel();
fruit.GetFruitInfo( " banana " , " 6.00 " );
}
Console.WriteLine( " The Client Is Running ... " );
Console.ReadLine();
fruitHost.Close();
}
static void OnGetFruitInfoCompleted( string fruitInfo)
{
Console.WriteLine(fruitInfo);
}
}
}
服务端服务寄存:
using System;
using System.ServiceModel;
using FruitSvc;
using IFruit;
namespace FruitResponseHost
{
class Program
{
static void Main( string [] args)
{
ServiceHost fruitServiceHost = new ServiceHost( typeof (FruitService),
new Uri( " net.msmq://localhost/private/GetFruitInfoQueue " ));
NetMsmqBinding netMsmqBind = new NetMsmqBinding();
netMsmqBind.Security.Mode = NetMsmqSecurityMode.None;
fruitServiceHost.AddServiceEndpoint( typeof (IFruitService), netMsmqBind, "" );
fruitServiceHost.Open();
Console.WriteLine( " The Service Is Running ... " );
Console.ReadLine();
fruitServiceHost.Close();
}
}
}
运行程序,先启动客户端,此时的消息可以通过MSMQ管理控制台进行管理:
从管理控制台可以看出GetFruitInfoQueue有一条消息,而后启动服务端:
客户端已经呈现出服务处理后的消息信息,GetFruitInfoQueue的消息已经被服务处理了,消息数目变为0。