跟我一起学WCF(11)——WCF中队列服务详解
一、引言
在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互。然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力。WCF通过服务队列的方法来支持客户端和服务之间的离线工作,客户端将消息发送到一个队列中,再由服务对它们进行处理。下面让我们具体看看WCF中的队列服务。
二、WCF队列服务的优势
在介绍WCF队列服务之前,首先需要了解微软消息队列(MSMQ)。MSMQ是在多个不同应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布在同一台机器,也可以分布在相连的网络环境。它的实现原理是:客户端将消息发送到一个容器中,然后把它保存到一个系统公用空间的消息队列(Message Queue)中,本地或异地的服务再从该队列中取出发送给它的消息进行处理。更多详细内容可以参考我的博文:跟我一起学WCF(1)——MSMQ消息队列。
WCF框架对MSMQ进行了集成和扩展,MSMQ支持离线消息模式,并且在WCF框架下,提供了基于http桥的internet网络队列服务的调用扩展。从而赋予了WCF队列服务以下几点优势:
- 支持离线消息模式。因为WCF框架集成了MSMQ,所以WCF队列服务自然也支持离线消息。
- 支持将操作分解。WCF支持将工作分解为多个操作放入队列中,可改善系统的可用性和吞吐量。
- 提供对失败的事务做善后处理。当我们的业务事务需要几个小时或几天完成的时候,我们通常将它分为至少2个事务。第一个事务将需要立即完成的工作放入队列,而第二个事务用于验证第一个事务是否成功,并在必要的情况下对失败的事务进行善后处理。
- 支持负载平衡。可以把过载的客户端请求放入队列,空闲的时候进行处理,这样可以平衡系统的吞吐量,改善性能。
三、WCF队列服务通信框架
WCF使用NetMsmqBinding来支持消息队列通信。当客户端调用服务时,客户端消息会被封装为MSMQ消息,发送到系统公用的消息队列中,服务宿主在运行状态下会启动通道监听器来检测消息队列消息,如果发现对应的消息,则会从队列里取出消息,使用分发器转发给对应的服务,具体的通信框架如下图所示:

如果宿主离线,消息会被放入队列,等待下一次宿主联机时,在执行消息分发给指定WCF服务处理。
另外WCF还提供了MsmqIntegrationBinding类,该类用于需要将WCF 应用和现有的基于MSMQ的应用集成的情况。WCF应用可利用该绑定向现有的MSMQ应用程序发生消息,或从这些应用程序接收消息。
四、利用WCF队列服务来实现离线操作
前面介绍WCF队列服务的优势和它的通信框架,下面具体通过一个例子来诠释WCF队列服务的实现。我们还是按照前面文章介绍的三个步骤来实现该实例。
第一步:定义契约和实现服务。具体的实现代码如下所示:
[ServiceContract]
public interface IWCFMSMQService
{
// 操作契约,必须为单向操作
[OperationContract(IsOneWay = true)]
void SayHello(string message);
} // 契约实现
public class WCFMSMQService : IWCFMSMQService
{
public WCFMSMQService()
{
Console.WriteLine("WCF MSMQ Service instance was created at: {0}", DateTime.Now);
} #region IOrderProcessor Members [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void SayHello(string message)
{
Console.WriteLine("Hello! {0},调用WCF操作的时间为:{1}", message, DateTime.Now);
} #endregion
}
上面代码需要注意一点:WCF操作必须定义为单向操作,因为要实现的是一个队列服务,其特点为异步、离线,无返回值。所以要设置IsOneWay属性为true。
第二步:实现宿主。这里仍然使用控制台应用程序作为WCF队列服务的宿主,具体的实现代码如下所示:
namespace WCFConsoleHost
{
class Program
{
static void Main(string[] args)
{
string path = @".\private$\LearningHardWCFMSMQ";
if (!MessageQueue.Exists(path))
{
MessageQueue.Create(path, true);
} using (ServiceHost host = new ServiceHost(typeof(WCFMSMQService)))
{
host.Opened += delegate
{
Console.WriteLine("Service has begun to listen\n\n");
}; host.Open(); Console.Read();
}
}
}
}
对应的配置信息如下所示:
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="msmqServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netMsmqBinding>
<binding name="msmqBinding">
<security>
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None"/>
<message clientCredentialType="None"/>
</security>
</binding>
</netMsmqBinding>
</bindings>
<services>
<service behaviorConfiguration="msmqServiceBehavior" name="WCFContractAndService.WCFMSMQService">
<endpoint address="net.msmq://localhost/private/LearningHardWCFMSMQ" binding="netMsmqBinding"
bindingConfiguration="msmqBinding" contract="WCFContractAndService.IWCFMSMQService" />
<!--发布服务元数据的终结点-->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9003/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
第三步:WCF客户端的实现。首先以管理员权限启动宿主,然后通过添加服务引用的方式来生成代理客户端类,具体在添加服务引用窗口地址栏输入:http://localhost:9003/mex来添加服务引用,添加服务引用成功后将生成代理类,通过代理类来对WCF服务进行访问,具体的实现代码如下所示:
namespace WCFClient
{
class Program
{
static void Main(string[] args)
{
WCFMSMQServiceClient proxy = new WCFMSMQServiceClient("NetMsmqBinding_IWCFMSMQService");
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
Console.WriteLine("WCF First Call at:{0}", DateTime.Now);
proxy.SayHello("World"); Thread.Sleep();//客户端休眠两秒,继续下一次调用
Console.WriteLine("WCF Second Call at:{0}", DateTime.Now);
proxy.SayHello("Learning Hard"); scope.Complete();
} Console.Read();
}
}
}
经过上面三步,我们就完成了一个WCF队列服务程序。下面让我们看看该程序的运行结果。
因为WCF队列服务是对MSMQ的集成和扩展,所以此时该程序客户端可以在WCF服务离线的情况也可运行,即WCF服务宿主不启动,客户端也可以正常运行,这点与前面介绍的WCF程序完成不同,因为前面的WCF程序,如果宿主程序不启动而直接启动客户端程序,则客户端程序运行时将会发生异常。该程序之所以不会发生异常的原因在于,此时客户端程序是直接与消息队列进行交互的,而不是直接与WCF服务进行交互。此时只运行WCF客户端,你将看到如下图所示的运行结果:

同时,你在消息队列的专有队列的队列消息中将看到两条未处理的消息信息,具体效果如下图所示:

因为WCF服务宿主还没有启动,所以客户端发送的消息信息还没有被取出处理,接下来,我们启动下服务宿主来对队列中的消息进行处理,此时你可以选择关闭客户端(当然你也可以不关闭)。具体的WCF服务宿主的运行结果如下图所示:

此时,如果刷新消息队列的专有队列中的队列消息,你将看不到任何消息了,这是因为WCF服务从消息队列取出消息进行处理了,即消息完成了出队的操作。
五、总结
到这里,WCF队列服务的介绍也就结束了。本文主要介绍了WCF集成了MSMQ的功能,WCF可以利用netMsmqBinding来实现离线服务。其实现程序与MSMQ程序实现差不多,关于MSMQ的相关内容也可以参考我的另一篇博文:跟我一起学WCF(1)——MSMQ消息队列。在下一篇博文中将分享WCF对Rest服务的支持和实现。
本文所有源码:WCFMSMQService.zip
跟我一起学WCF(11)——WCF中队列服务详解的更多相关文章
- WCF中队列服务详解
WCF中队列服务详解 一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务 ...
- Android中Service(服务)详解
http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...
- 缓存架构中的服务详解!SpringBoot中二级缓存服务的实现
创建缓存服务 创建缓存服务接口项目 创建myshop-service-redis-api项目,该项目只负责定义接口 创建项目的pom.xml: <?xml version="1.0&q ...
- oracle中imp命令详解 .
转自http://www.cnblogs.com/songdavid/articles/2435439.html oracle中imp命令详解 Oracle的导入实用程序(Import utility ...
- python中threading模块详解(一)
python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...
- (转)javascript中event对象详解
原文:http://jiajiale.iteye.com/blog/195906 javascript中event对象详解 博客分类: javaScript JavaScriptCS ...
- iOS中—触摸事件详解及使用
iOS中--触摸事件详解及使用 (一)初识 要想学好触摸事件,这第一部分的基础理论是必须要学会的,希望大家可以耐心看完. 1.基本概念: 触摸事件 是iOS事件中的一种事件类型,在iOS中按照事件划分 ...
- 【JavaScript中的this详解】
前言 this用法说难不难,有时候函数调用时,往往会搞不清楚this指向谁?那么,关于this的用法,你知道多少呢? 下面我来给大家整理一下关于this的详细分析,希望对大家有所帮助! this指向的 ...
- JavaScript中的this详解
前言 this用法说难不难,有时候函数调用时,往往会搞不清楚this指向谁?那么,关于this的用法,你知道多少呢? 下面我来给大家整理一下关于this的详细分析,希望对大家有所帮助! this指向的 ...
随机推荐
- Salt官方将RHEL5/CentOS5 源
Salt官方将RHEL5/CentOS5的软件包维护迁移到了Fedora Corp (https://copr.fedoraproject.org/coprs/saltstack/salt-el5/) ...
- centos环境下使用percona-xtrabackup对mysql5.6数据库innodb和myisam进行快速备份及恢复
centos环境下使用percona-xtrabackup对mysql5.6数据库innodb和myisam进行快速备份及恢复 有时候我们会碰到这样的业务场景: 1.将大的数据库恢复到本地进行业务测试 ...
- delphi 10 seattle 安卓服务开发(三)
delphi 10 里面的安卓服务有四种,上面的一篇文章里面的图有介绍. 今天做一个remote service 的例子.(里面一部分代码是抄别人的,如果不太清楚,自行恶补) remote servi ...
- iOS nib file owner
nib文件中的file owner属性,设定后app在运行时加载nib文件的过程中会通过file owner重新建立nib文件中描述的控件与其在file owner中对应的IBOutlet或IBAct ...
- IOS-Appium 自动化测试——环境配置及模拟器、真机跑测试
在MAC环境下配置IOS的appium的自动化测试环境,主要包含三个部分: 一.环境配置 1.安装homebrew(homebrew可以提供MAC OS无法提供的很多套件) ruby -e " ...
- MySql表名的大小写问题
MySQL在Linux下数据库名.表名.列名.别名大小写规则是这样的: 1.数据库名与表名是严格区分大小写的: 2.表的别名是严格区分大小写的: 3.列名与列的别名在所有的情况下均是忽略大小写的: 4 ...
- 心情闲适,发几个tatanic的图
第一次看这个是98年在高一的同学家里. 唯一的月末休息时,那时没有电话,老父以为我会在下午到caojp,结果老父在寒风中等我一个下午,发火了.
- hdu 5666 (大数乘法) Segment
题目:这里 题意:在线段x+y=q与坐标轴围成的三角形中,求有多少个坐标为整数的点,答案模上p. 很容易就想到最后答案就是((q-1)*(q-2))/2然后模上p就是了,但是这个数字比较大,相乘会爆l ...
- 缺jstl.jar包导致的代码出现异常
java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config 看报错中的红色部分,意思是缺类异常,再看后面蓝色粗体倾斜部分, ...
- 将asp.net页面弄成伪静态
在Web.config中写: <RewriterConfig> <Rules> <RewriterRule> <LookFor> ...