消息隊列不同於傳統的請求響應模式,它是客戶端把消息發送給請求消息隊列,服務可以稍後對消息進行處理並把處理結果發送給響應隊列,而後客戶端從響應隊列讀取服務處理後的消息。而且使用消息隊列可以使客戶端實現脫機工作。脫機應用程序必須有本地緩存數據,要採用異步通訊而且要把消息持久化,在與服務器聯機後將消息發送出去。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。