WCF中队列服务详解

一、引言

  在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互。然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力。WCF通过服务队列的方法来支持客户端和服务之间的离线工作,客户端将消息发送到一个队列中,再由服务对它们进行处理。下面让我们具体看看WCF中的队列服务。

二、WCF队列服务的优势

  在介绍WCF队列服务之前,首先需要了解微软消息队列(MSMQ)。MSMQ是在多个不同应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布在同一台机器,也可以分布在相连的网络环境。它的实现原理是:客户端将消息发送到一个容器中,然后把它保存到一个系统公用空间的消息队列(Message Queue)中,本地或异地的服务再从该队列中取出发送给它的消息进行处理。更多详细内容可以参考我的博文:跟我一起学WCF(1)——MSMQ消息队列

  WCF框架对MSMQ进行了集成和扩展,MSMQ支持离线消息模式,并且在WCF框架下,提供了基于http桥的internet网络队列服务的调用扩展。从而赋予了WCF队列服务以下几点优势:

  1. 支持离线消息模式。因为WCF框架集成了MSMQ,所以WCF队列服务自然也支持离线消息。
  2. 支持将操作分解。WCF支持将工作分解为多个操作放入队列中,可改善系统的可用性和吞吐量。
  3. 提供对失败的事务做善后处理。当我们的业务事务需要几个小时或几天完成的时候,我们通常将它分为至少2个事务。第一个事务将需要立即完成的工作放入队列,而第二个事务用于验证第一个事务是否成功,并在必要的情况下对失败的事务进行善后处理。
  4. 支持负载平衡。可以把过载的客户端请求放入队列,空闲的时候进行处理,这样可以平衡系统的吞吐量,改善性能。

三、WCF队列服务通信框架

  WCF使用NetMsmqBinding来支持消息队列通信。当客户端调用服务时,客户端消息会被封装为MSMQ消息,发送到系统公用的消息队列中,服务宿主在运行状态下会启动通道监听器来检测消息队列消息,如果发现对应的消息,则会从队列里取出消息,使用分发器转发给对应的服务,具体的通信框架如下图所示:

  如果宿主离线,消息会被放入队列,等待下一次宿主联机时,在执行消息分发给指定WCF服务处理。

  另外WCF还提供了MsmqIntegrationBinding类,该类用于需要将WCF 应用和现有的基于MSMQ的应用集成的情况。WCF应用可利用该绑定向现有的MSMQ应用程序发生消息,或从这些应用程序接收消息。

四、利用WCF队列服务来实现离线操作

  前面介绍WCF队列服务的优势和它的通信框架,下面具体通过一个例子来诠释WCF队列服务的实现。我们还是按照前面文章介绍的三个步骤来实现该实例。

  第一步:定义契约和实现服务。具体的实现代码如下所示:

 1 [ServiceContract]
2 public interface IWCFMSMQService
3 {
4 // 操作契约,必须为单向操作
5 [OperationContract(IsOneWay = true)]
6 void SayHello(string message);
7 }
8
9 // 契约实现
10 public class WCFMSMQService : IWCFMSMQService
11 {
12 public WCFMSMQService()
13 {
14 Console.WriteLine("WCF MSMQ Service instance was created at: {0}", DateTime.Now);
15 }
16
17 #region IOrderProcessor Members
18
19 [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
20 public void SayHello(string message)
21 {
22 Console.WriteLine("Hello! {0},调用WCF操作的时间为:{1}", message, DateTime.Now);
23 }
24
25 #endregion
26 }

  上面代码需要注意一点:WCF操作必须定义为单向操作,因为要实现的是一个队列服务,其特点为异步、离线,无返回值。所以要设置IsOneWay属性为true。

  第二步:实现宿主。这里仍然使用控制台应用程序作为WCF队列服务的宿主,具体的实现代码如下所示:

 1 namespace WCFConsoleHost
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 string path = @".\private$\LearningHardWCFMSMQ";
8 if (!MessageQueue.Exists(path))
9 {
10 MessageQueue.Create(path, true);
11 }
12
13 using (ServiceHost host = new ServiceHost(typeof(WCFMSMQService)))
14 {
15 host.Opened += delegate
16 {
17 Console.WriteLine("Service has begun to listen\n\n");
18 };
19
20 host.Open();
21
22 Console.Read();
23 }
24 }
25 }
26 }

  对应的配置信息如下所示:

<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服务进行访问,具体的实现代码如下所示:

 1 namespace WCFClient
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 WCFMSMQServiceClient proxy = new WCFMSMQServiceClient("NetMsmqBinding_IWCFMSMQService");
8 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
9 {
10 Console.WriteLine("WCF First Call at:{0}", DateTime.Now);
11 proxy.SayHello("World");
12
13 Thread.Sleep(2000);//客户端休眠两秒,继续下一次调用
14 Console.WriteLine("WCF Second Call at:{0}", DateTime.Now);
15 proxy.SayHello("Learning Hard");
16
17 scope.Complete();
18 }
19
20 Console.Read();
21 }
22 }
23 }

  经过上面三步,我们就完成了一个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中队列服务详解的更多相关文章

  1. 跟我一起学WCF(11)——WCF中队列服务详解

    一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务队列的方法来支持客户端 ...

  2. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  3. 缓存架构中的服务详解!SpringBoot中二级缓存服务的实现

    创建缓存服务 创建缓存服务接口项目 创建myshop-service-redis-api项目,该项目只负责定义接口 创建项目的pom.xml: <?xml version="1.0&q ...

  4. winxp计算机管理中服务详解

    winxp计算机管理中服务详解01 http://blog.sina.com.cn/s/blog_60f923b50100efy9.html http://blog.sina.com.cn/s/blo ...

  5. (转)python标准库中socket模块详解

    python标准库中socket模块详解 socket模块简介 原文:http://www.lybbn.cn/data/datas.php?yw=71 网络上的两个程序通过一个双向的通信连接实现数据的 ...

  6. Android中Intent组件详解

    Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...

  7. Liunx中fstab文件详解

    Liunx中fstab文件详解 /etc/fstab是用来存放文件系统的静态信息的文件.位于/etc/目录下,可以用命令less /etc/fstab 来查看,如果要修改的话,则用命令 vi /etc ...

  8. 【转】SSH服务详解

    [转]SSH服务详解 第1章 SSH服务 1.1 SSH服务协议说明 SSH 是 Secure Shell Protocol 的简写,由 IETF 网络工作小组(Network Working Gro ...

  9. springcloud中Feign配置详解

    Spring Cloud中Feign配置详解 到目前为止,小伙伴们对Feign的使用已经掌握的差不多了,我们在前文也提到Feign是对Ribbon和Hystrix的整合,那么在Feign中,我们要如何 ...

随机推荐

  1. 最大流量dinci模板

    我们知道.增广路径EK时间是在充电算法的O(n*m^2).找到最短增广路径的时间复杂度为O(m*n^2).这样的时间复杂度主要是寻找扩充道路. 这里也有一个演示Dinci算法,使用BFS层次结构图,然 ...

  2. Android.mk中的经常使用语法

    Android.mk编译文件是用来向Android NDK描写叙述你的C,C++源码文件的, 今天查了一些经常使用的的语法. 一 概述: 一个Android.mk文件用来向编译系统描写叙述你的源码. ...

  3. HDU-4628 Pieces 如压力DP

    鉴于他的字符串,每一个都能够删除回文子串.子可以是不连续,因此,像更好的模拟压力.求删除整个字符串需要的步骤的最小数量. 最大长度为16,因此不能逐行枚举状态.首先预处理出来全部的的回文子串,然后从第 ...

  4. remine chart2安装

    http://blog.csdn.net/kufeiyun/article/details/9213911

  5. 【SSH 基金会】SSH框架--struts进一步的详细解释(两)

    继上篇博客 既然我们知道了不使用struts给我们带来这么多弊端,那么以下我们来看看struts是怎样封装的.怎么解决我们出现的问题的? 先来说一下struts的基本流程,帮助大家理解以下的代码: S ...

  6. OpenGL于MFC使用汇总(三)——离屏渲染

    有时直接创建OpenGL形式不适合,或者干脆不同意然后创建一个表单,正如我现在这个项目,创建窗体不显示,它仅限于主框架.而我只是ActiveX里做一些相关工作,那仅仅能用到OpenGL的离屏渲染技术了 ...

  7. cidaemon.exe是什么进程及怎样关闭cidaemon.exe进程

    问题描写叙述: 这段时间机器总是出现一个奇怪的问题:cidaemon.exe进程占用CUP率98%以上,大大影响了电脑的正常使用.资源管理器中出现多个cidaemon.exe进程,强制结束占用cpu率 ...

  8. asp.net学习之数据绑定控件、数据源控件概述

    原文:asp.net学习之数据绑定控件.数据源控件概述 1.asp.net数据绑定控件分为三大类,每个类分别进行详细:      ● 列表式数据绑定控件: 列表式数据绑定控件常用来在一个表格内的一个字 ...

  9. JavaScript循环之for/in循环

    今天学到了JavaScript的语句篇.同其他常见编程语言如C.Java等一样,JavaScript中的语句包含:①表达式语句②复合语句和空语句③声明语句④条件语句⑤循环语句⑥跳转语句,当然JavaS ...

  10. JavaScript编写了一个计时器

    初学JavaScript,用JavaScript编写了一个计时器. 设计思想: 1.借助于Date()对象,来不断获取时间点: 2.然后用两次时间点的毫秒数相减,算出时间差: 3.累加时间差,这样就能 ...