MSMQ 为使用队列创建分布式应用程序提供支持。WCF支持将MSMQ队列作为netMsmqBinding绑定的底层传输协议的通信。netMsmqBinding绑定允许客户端直接把消息提交到一个队列中同时服务端从队列中读取消息。客户端和服务端之间没有直接通信过程;因此,通信本质是断开的。也意外着所有的通信必须是单向的。因此,所有的操作必须要在操作契约上设置IsOneWay=true属性。
提示 动态创建队列
使用netMsmqBinding时动态创建MSMQ队列是很普通的。当创建一个离线客户端应用而且队列在一个用户的桌面时使用netMsmqBinding绑定更加平常。这可以通过创建System.MessageQueue类的静态方法来实现。
下面的代码显示了netMsmqBinding绑定的地址格式:
net.msmq:{hostname}/[private/|[public/]]{query name}
MSMQ默认端口是1801而且没有配置解决方案。注意地址格式中的public和private.你可以显式的确定是否队列名字指向一个私有的或者公有的队列。默认情况下,队列名字假设指向一个公共队列。
表4.11 netMsmqBinding 绑定属性
我们在列表4.2到4.4使用的StockQuoteService样例程序需要被修改以便于与netMsmqBinding绑定一起工作。netMsmqBinding绑定仅支持单向操作(查看表4.2).我们之前的操作契约使用一个请求回复消息交换模式(查看列表4.4).我们将修改StockQuoteService例子来显示基于netMsmqBinding绑定的双向通信而不是显示一个不同的例子。
我们需要使用两个单向操作契约来维护服务端和客户端的双向通信。这意味着我们需要重新定义我们的契约以便于使用netMsmqBinding绑定。列表4.24显示了写来与netMsmqBinding绑定一起使用的stock quote 契约。首先,注意我们把请求和回复契约转换成两个独立的服务契约:IStockQuoteRequest和IStockQuoteResponse.每个契约上的操作都是单向的。IStockQuoteRequest契约将被客户端用来向服务端发送消息。IStockQuoteResponse契约将被服务端用来发送一条消息给客户端。这意味着客户端和服务端都将寄宿服务来接收消息。
列表 4.24 IStockQuoteRequest,IStockQuoteResponse和StockQuoteRequestService
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; namespace EssentialWCF { [ServiceContract] public interface IStockQuoteRequest { [OperationContract(IsOneWay=true)] void SendQuoteRequest(string symbol); } [ServiceContract] public interface IStockQuoteResponse { [OperationContract(IsOneWay = true)] void SendQuoteResponse(string symbol, double price); } public class StockQuoteRequestService : IStockQuoteRequest { public void SendQuoteRequest(string symbol) { double value; if (symbol == "MSFT") value = 31.15; else if (symbol == "YHOO") value = 28.10; else if (symbol == "GOOG") value = 450.75; else value = double.NaN; //Send response back to client over separate queue NetMsmqBinding msmqResponseBinding = new NetMsmqBinding(); using (ChannelFactory<IStockQuoteResponse> cf = new ChannelFactory<IStockQuoteResponse>("NetMsmqResponseClient")) { IStockQuoteResponse client = cf.CreateChannel(); using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { client.SendQuoteResponse(symbol, value); scope.Complete(); } cf.Close(); } } } }
netMsmqBinding下一个要考虑的就是使用ServiceHost类。先前的例子可以在不同的绑定上重用相同的ServiceHost代码。这因为服务契约可以保持一样。而不是因为使用了netMsmqBinding。更新的用来寄宿StockServiceRequestService服务的ServiceHost代码在列表4.25中显示。我们已经更新代码来动态创建一个在基于配置文件中queueName的MSMQ队列。这有助于通过简单配置允许程序部署而不需要额外的MSMQ配置。
列表 4.25 StockQuoteRequestService ServiceHost 服务
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Configuration; using System.Messaging; namespace EssentialWCF { class Program { static void Main(string[] args) { MyServiceHost.StartService(); Console.WriteLine("Service is Started, press Enter to terminate."); Console.ReadLine(); MyServiceHost.StopService(); } } internal class MyServiceHost { internal static string queryName = string.Empty; internal static ServiceHost myServiceHost = null; internal static void StartService() { queryName = ConfigurationManager.AppSettings["queueName"]; if (!MessageQueue.Exists(queryName)) MessageQueue.Create(queryName, true); myServiceHost = new ServiceHost(typeof(EssentialWCF.StockQuoteRequestService)); myServiceHost.Open(); } internal static void StopService() { if (myServiceHost.State != CommunicationState.Closed) myServiceHost.Close(); } } }
列表4.26的配置信息使用netMsmqBinding绑定暴露StockQuoteRequestService服务。它也为IStockQuoteResponse契约配置一个客户端终结点以便于回复可以发送给客户端。
列表 4.26 netMsmqBinding 宿主 配置
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <client> <endpoint address="net.msmq://localhost/private/stockquoteresponse" binding="netMsmqBinding" bindingConfiguration="NoMsmqSecurity" contract="EssentialWCF.IStockQuoteResponse" name="NetMsmqResponseClient" /> </client> <bindings> <netMsmqBinding> <binding name="NoMsmqSecurity"> <security mode="None" /> </binding> </netMsmqBinding> </bindings> <services> <service name="EssentialWCF.StockQuoteRequestService"> <endpoint address="net.msmq://localhost/private/stockquoterequest" binding="netMsmqBinding" bindingConfiguration="NoMsmqSecurity" name="" contract="EssentialWCF.IStockQuoteRequest" /> </service> </services> </system.serviceModel> <appSettings> <add key="queueName" value=".\private$\stockquoterequest"/> </appSettings> </configuration>
客户端应用程序必须使用netMsmqBinding寄宿一个服务来接受回复且配置一个终结点来发送请求给服务端。列表4.27 显示了客户端用来寄宿一个实现了IStockQuoteResponse契约的ServiceHost类。我们添加代码来动态创建一个客户端监听的队列。再次,这有助于通过简单配置允许程序部署而不需要额外的MSMQ配置。
列表 4.27 StockQuoteResponseService ServiceHost 客户端
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Configuration; using System.Messaging; namespace EssentialWCF { internal class MyServiceHost { internal static ServiceHost myServiceHost = null; internal static void StartService() { string queryName = ConfigurationManager.AppSettings["queueName"]; if (!MessageQueue.Exists(queryName)) MessageQueue.Create(queryName, true); myServiceHost = new ServiceHost(typeof(EssentialWCF.Program)); myServiceHost.Open(); } internal static void StopService() { if (myServiceHost.State != CommunicationState.Closed) myServiceHost.Close(); } } }
列表4.28 显示了IStockQuoteResponse接口的客户端实现。客户端实现了接口,接下来被服务端当作发送回复的回调端。这不是使用了WCF中的双向能力。相反的,回调使用一个单独的单向绑定实现。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.ServiceModel; using System.Transactions; namespace EssentialWCF { public class Program : IStockQuoteResponse { private static AutoResetEvent waitForResponse; static void Main(string[] args) { //Start response service host MyServiceHost.StartService(); try { waitForResponse = new AutoResetEvent(false); //Send request to the server using (ChannelFactory<IStockQuoteRequest> cf = new ChannelFactory<IStockQuoteRequest>("NetMsmqRequestClient")) { IStockQuoteRequest client = cf.CreateChannel(); using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { client.SendQuoteRequest("MSFT"); scope.Complete(); } cf.Close(); } waitForResponse.WaitOne(); } finally { MyServiceHost.StopService(); } Console.ReadLine(); } public void SendQuoteResponse(string symbol, double price) { Console.WriteLine("{0}@${1}", symbol, price); waitForResponse.Set(); } } }
让netMsmqBinding Stock Quote 样例工作起来的最后一步是客户端配置文件。列表4.29 显示了客户端配置,包含了寄宿IStockQuoteResponse服务实现的信息,调用IStockQuoteRequest服务的终结点配置。
列表 4.29 netMsmqBinding 客户端配置
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <netMsmqBinding> <binding name="NoMsmqSecurity"> <security mode="None" /> </binding> </netMsmqBinding> </bindings> <client> <endpoint address="net.msmq://localhost/private/stockquoterequest" binding="netMsmqBinding" bindingConfiguration="NoMsmqSecurity" contract="EssentialWCF.IStockQuoteRequest" name="NetMsmqRequestClient" /> </client> <services> <service name="EssentialWCF.StockQuoteRequestService"> <endpoint address="net.msmq://localhost/private/stockquoteresponse" binding="netMsmqBinding" bindingConfiguration="NoMsmqSecurity" contract="EssentialWCF.IStockQuoteResponse" /> </service> </services> </system.serviceModel> <appSettings> <add key="queueName" value=".\private$\stockquoteresponse"/> </appSettings> </configuration>