一、引言

  在前面的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队列服务的实现。我们还是按照前面文章介绍的三个步骤来实现该实例。

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

 [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中队列服务详解的更多相关文章

  1. WCF中队列服务详解

    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. oracle中imp命令详解 .

    转自http://www.cnblogs.com/songdavid/articles/2435439.html oracle中imp命令详解 Oracle的导入实用程序(Import utility ...

  5. python中threading模块详解(一)

    python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...

  6. (转)javascript中event对象详解

    原文:http://jiajiale.iteye.com/blog/195906 javascript中event对象详解          博客分类: javaScript JavaScriptCS ...

  7. iOS中—触摸事件详解及使用

    iOS中--触摸事件详解及使用 (一)初识 要想学好触摸事件,这第一部分的基础理论是必须要学会的,希望大家可以耐心看完. 1.基本概念: 触摸事件 是iOS事件中的一种事件类型,在iOS中按照事件划分 ...

  8. 【JavaScript中的this详解】

    前言 this用法说难不难,有时候函数调用时,往往会搞不清楚this指向谁?那么,关于this的用法,你知道多少呢? 下面我来给大家整理一下关于this的详细分析,希望对大家有所帮助! this指向的 ...

  9. JavaScript中的this详解

    前言 this用法说难不难,有时候函数调用时,往往会搞不清楚this指向谁?那么,关于this的用法,你知道多少呢? 下面我来给大家整理一下关于this的详细分析,希望对大家有所帮助! this指向的 ...

随机推荐

  1. delegate事件绑定

    为了代码的健壮性,绑定事件之前先解绑再进行绑定. var _$div = $("#id");_$div.undelegate("click mouseover mouse ...

  2. 使用css3中calc()进行自适应布局

    calc()能做什么? calc()可以通过计算得到元素的宽度或者高度,让我们很容易进行自适应布局. 你可以为一个div元素,使用百分比.em.px和rem单位值计算出其宽度或者高度,比如说“widt ...

  3. 编程范式 episode3 and 4,5

    episode 3 --storage structure. ampersand operate with asterisk --library function episode 4 --generi ...

  4. LESS与SASS的伯与仲

    工作中用到了Bootstrap,涉及到了LESS,对其做了一个简单的了解,CSS的预处理器使用最广泛的就是LESS和Sass,都是努力把CSS武装成为开发语言,让它从简单的描述性语言过渡到具有程序式特 ...

  5. 装完RHEL7后,重新开机启动后显示:Initial setup of CentOS Linux 7 (core) 提示license报错

    装完RHEL7后,重新开机启动后显示: 1) [x] Creat user 2) [!] License information (no user will be created) (license ...

  6. android笔记:DatePickerDialog日期设置对话框

    在开发中,可以通过DatePickerDialog来设置日期,TimePickerDialog来设置时间. 实例化DatePickerDialog对象之后,再调用show方法就可以显示对话框了. 具体 ...

  7. NGUI Draw Calls优化(思路)

    用NGUI做界面的时候发现不注意GameObject(或者说Widget)的depth的话,单独运行界面时,Draw Calls挺高的: 网上搜了一下,大把的博客说的都是类似以下的原则: (PS:以下 ...

  8. @gettrcname.sql

    http://www.eygle.com/archives/2007/05/script_gettrcname.html 最近有很多朋友问起<深入浅出Oracle>一书中的一个脚本gett ...

  9. 20145225唐振远 实验二 "Java面向对象程序设计"

    20145225<Java程序设计> 实验二 Java面向对象程序设计 实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S ...

  10. FreeBSD_11 - 系统管理——{ Part_5 - ZFS }

    参考資料 http://docs.oracle.com/cd/E37934_01/html/E36658/toc.html https://www.freebsd.org/doc/en_US.ISO8 ...