WCF揭秘学习笔记(4):可信赖会话、会话管理、队列、事务
可信赖会话
WCF的可信赖会话在绑定层保证消息只会被传输一次,并且保证消息间的顺序。当使用TCP通信时,协议本身保证了可靠性,但它只在两点间的网络 包这个层面提供了这样的保证。WCF的可信赖会话特性保证了在传输过程中消息不会丢失、重复或错位。这种保证是消息层面的,且适用于任何数目节点间的通 信。另外,使用可信赖会话时,WCF会重连掉线的连接,在重连失败时还会释放会话占用的相关资源。可信赖会话还会通过调整消息的发送频率来缓解网络拥挤。
为使用WCF的可信赖会话,必须选择支持可信赖会话的绑定。支持这一特性的预定义绑定包括WSHttpBinding、 WSDualHttpBinding、WSFederationBinding、NetTcp-Binding和 NetNamedPipesBinding。在WSHttpBinding、WSDualHttpBinding和 WSFederationBinding的情况下,可信赖特性默认是关闭的,对其他绑定而言,默认是打开的。打开或关闭可信赖会话特性只要按如下方式自定 义绑定即可:
<system.serviceModel>
<services>
<service>
<endpoint binding="wsHttpBinding" bindingConfiguration="MyReliableConfiguration"
[...]
/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="MyReliableConfiguration">
<reliableSession enabled="true" ordered="true"/>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
也可以通过包含System.ServiceModel.Channels.ReliableSessionBindingElement绑定元素为自定制的绑定添加可信赖会话特性。
<system.serviceModel>
<services>
<service name="[...]">
<endpoint
[...]
binding="customBinding"
bindingConfiguration="MyReliableCustomBinding">
</endpoint>
</service>
</services>
<bindings>
<customBinding>
<binding name="MyReliableCustomBinding">
<reliableSession ordered="false"/>
<httpTransport />
</binding>
</customBinding>
</bindings>
</system.serviceModel>
WCF开发者可以指出他们的代码将依赖于对消息传送的保证。比如,他们可以指出将假定消息按照它们发出的顺序传送。
[ServiceContract(SessionMode=SessionMode.Required)]
[DeliveryRequirements(RequireOrderedDelivery=true)]
publicinterface IMyServiceContract
为服务契约添加这样的定义将导致WCF检查任何包含该服务契约的终结点,以确认它们选择了支持可信赖会话的绑定,并且被正确地配置以保证按顺序传送。
会话管理
会话管理允许WCF应用程序将其接收到的消息作为会话的一部分来对待,也就是作为与另一个应用程序进行交换的统一序列消息的一部分。
所以,WCF的应用程序开发者就可以通过如下的方式来编码处理消息:一个消息的处理需要依靠前一个消息的信息。如果需要编写这样的代码,可以将 System.Service-ModelServiceContract特性的SessionMode参数设为 System.ServiceModel.SessionMode.Required值:
[ServiceContract(SessionMode=SessionMode.Required)]
publicinterface IMyServiceContract
这将让WCF检查为契约中包含的终结点选择的绑定是否支持会话,并将信息合并到消息中以识别会话。支持该功能的预定义绑定包括 WSHttpBinding、WSDualHttp-Binding、WSFederationBinding、NetTcpBinding、 NetNamedPipesBinding和NetMsmqBinding。
开发者可以在实例上下文会话中存储或检索与会话相关的数据:
publicclass MyExtension : IExtension<InstanceContext>
{
publicstring sessionIdentifier =null;
public MyDataType MyData =null;
} publicvoid FirstRequest(MyDataType myData)
{
MyExtension extension =new MyExtension();
extension.sessionIdentifier = OperationContext.SessionId;
extension.MyDataType = myData;
OperationContext.InstanceContext.Extensions.Add(myData);
} public MyDataType SubsequentRequest()
{
Collection<MyExtension> extensions = OperationContext.InstanceContext.
Extensions.FindAll<MyExtension>();
foreach(MyExtension extension in extensions)
{
if(string.Compare(extension.sessionIdentifier, OperationContext.SessionId, true)==)
return extension.MyData;
}
returnnull;
}
为更好地管理会话的相关资源,开发者可指定哪些操作开始会话,哪些操作结束会话:
[ServiceContract(SessionMode=SessionMode.Required)]
publicinterface IMyServiceContract
{
[OperationContract(IsInitiating=true)]
void StartSession();
[OperationContract(IsTerminating=true)]
void StopSession();
}
队列交付
可信赖会话提供的消息传送保证只能作用在宿主程序域的生命周期里。如果一个消息在传送过程中丢失了,但发送消息的程序域在发现消息丢失前终止了,当程序域重新运行后,它就不知道这个消息已经丢失了。实际上,它将丢掉整个被丢失的消息所在会话的上下文。
WCF能够通过MSMQ(Microsoft Message Queuing,Microsoft消息队列)发送消息,这为消息传送提供了独立于发送和接收应用程序域的生命周期的保证。队列代表接收应用程序存储发送 应用程序发出的消息。在消息已经存储到队列之后的某个时间,接收应用程序将获得这个消息。
优先:
1. 如果接收应用程序因为某些原因(如网络中断等)不能接收消息,发送应用程序仍可以发送消息然后继续做其他事件。消息将一直放在队列里,直到接收应用程序可以接收这些消息为止。
2. 发送和接收应用程序间的网络带宽并不是一致的,使用MSMQ的主要限制是MSMQ只能存储小于4MB的消息。
3. 接收应用程序不应该被不可预测的大量请求压垮。应用程序可以从队列里以它希望的速度获取消息。
4. MSMQ是一项大多数系统管理员都熟悉的技术,操作系统也提供了Microsoft Management Console管理单元来管理MSMQ。
队列可以用在两个WCF应用程序间的通信,也可以用于WCF应用程序与一个使用MSMQ的非WCF应用的通信。
为使用MSMQ队列从一个WCF应用程序发送消息到另一个WCF应用程序,可以使用预定义的NetMsmqBinding。
<services>
<service name="Fabrikam.TradeRecorder">
<host>
<baseAddresses>
<add baseAddress="net.msmq://localhost/private/"/>
</baseAddresses>
</host>
<endpoint address="EndpointAddress" binding="netMsmqBinding" [...] />
</service>
</services>
使用预定义NetMsmqBinding绑定的终结点,必须在地址里使用net.msmq策略。地址的下一段指定了队列所在的主机。如果队列是私有的,那么private段是必需的。最后一段是终结点自身的地址,它必须是指定主机上的事务性MSMQ队列的名称。
被配置成通过MSMQ队列接收消息的服务必须和队列本身部署在同一台机器上。这个限制是由于MSMQ只允许对非事务队列进行远程读取,而WCF只能使用NetMsmq-Binding绑定从事务队列接收消息。
可以配置一个服务的多个实例从同一个队列接收消息。在这种情况下,最空闲的应用程序将接收到下一个消息。
当一个终结点被配置为从队列获取消息的预定义绑定,WCF将确认终结点契约的所有操作都显式地声明为单向(one-way)。
publicinterface IMyServiceContract
{
[OperationContract(IsOneWay=true)]
void FirstOperation(string input);
[OperationContract(IsOneWay=true)]
void SecondOperation(string input);
}
WCF应用程序的开发者可以为服务契约添加一个特性以指定应用程序将通过队列接收消息。
[ServiceContract(SessionMode=SessionMode.Required)]
[DeliveryRequirements(QueueDeliveryRequirements=QueueDeliveryRequirementsMode.Required]
publicinterface IMyServiceContract
如果开发者这样做,WCF将确认包含这些服务契约的任何终结点的绑定都是通过队列将消息传送给服务的绑定。
Windows Vista的改进
Windows Vista和其之后的操作系统对MSMQ进行了一些改进,这些改进与dead-letter和有毒队列(poison queue)有关。
dead-letter队列
MSMQ消息可以配置要到达的目的队列和从目的队列接收的时间。当这些时间中任何一个过期时,消息将会放到一个被称作dead-letter队 列的系统消息队列中。另外,如果消息被发往事务队列,而发送端的队列管理器没有接收到消息已经从目的队列中读取的肯定确认时,这个消息将被转移到一个事务 dead-letter队列。在Windows Vista和其后的操作系统里,可以创建一个队列并将其指定为另一个队列的dead-letter队列。WCF提供了这项改进的支持,开发者可以为使用队 列发送和接收消息的应用程序指定另一个队列作为dead-letter队列。
<client>
<endpoint address="net.msmq://localhost/private$/EndpointAddress"
binding="netMsmqBinding" bindingConfiguration="QueueBinding"
[...] />
</client>
<bindings>
<netMsmqBinding>
<binding name="QueueBinding" deadLetterQueue="Custom"
customDeadLetterQueue="net.msmq://localhost/private$/myDeadLetterQueue">
</binding>
</netMsmqBinding>
</bindings>
有毒队列
有毒消息是事务队列中不能被接收应用程序处理的消息。当消息从队列中读取而接收应用程序不能处理它时,应用程序会回滚读取这个消息的事务,这个消息也将会被放回到队列中。应用程序再一次读取这个消息,读取和回滚该有毒消息的过程就会无限循环下去。
在Windows Vista之前,MSMQ本身并没有提供检测或复制有毒消息的功能。WCF为此提供了帮助机制,开发者可以为ReceiveRetryCount和ReceiveErrorHanding属性指定合适的值。
<services>
<service name="Fabrikam.TradeRecorder">
<host>
<baseAddress>
<add baseAddress="net.msmq://localhost/private/"/>
</baseAddress>
</host>
<endpoint address="EndpointAddress" binding="netMsmqBinding"
bindingConfiguration="QueueBinding"
[...] />
</service>
</services>
<bindings>
<netMsmqBinding>
<binding name="QueueBinding" receiveRetryCount="0" receiveErrorHandling="Fault">
</binding>
</netMsmqBinding>
</bindings>
ReceiveRetryCount属性的值定义了什么样的消息是有毒消息--就是那些回滚到队列的次数超过 ReceiveRetryCount属性值的消息。ReceiveErrorHanding属性的值指定了对有毒消息的操作。它的值的选择包含将服务改成 出错状态从而不能继续接收任何消息,或者忽略这个有毒消息消息等。
Windows Vista和之后的操作系统为此提供了更多的选项。把ReceiveErrorHandling的值设为Move会将有毒消息返回到其发送源。这种情况 下,消息最后会被放入发送源的dead-letter队列里。还有一个选项,把ReceiveErrorHandling的值设为Reject,有毒消息 将会被移到接收队列的一个有毒子队列中。对于一个地址为net.msmq://local-host/privateEndpointAddress的队 列而言,其有毒子队列的地址为net.msmq://localhost/private/EndpointAddress:Poison。
事务
WCF在预定义绑定中实现了标准的WSAtomicTransaction(WS-AT)协议和Microsoft专有的OleTx协议,这些协议可以用来在消息中加入事务状态的信息。WCF的开发者可以指定将一个操作的代码放在一个事务范围里执行。
[ServiceContract]
publicinterface IMyServiceContract
{
[OperationContract(IsOneWay=false)]
[TransactionFlow(TransactionFlowOption.Required)]
void MyMethod();
} publicclass MyServiceType : IMyServiceContract
{
[OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=true)]
vod MyMethod()
{
[...]
}
}
任何被指定的必须在一个事务范围里执行的操作都不能被标记为单向方法,因为在操作结束时,有关事务状态的信息必须传回给调用者。TransactionAutoComplete特性指出没有异常发生时,WCF应该自动为操作提交事务。
如果开发者决定由操作自己来提交事务,可使用WCF的静态System.ServiceModel.OperationContext对象:
publicclass MyServiceType
{
[OperationBehavior(TransactionScopeRequired=true, TaansactionAutoComplete=false)]
void MyMethod()
{
//Work gets done here
OperationContext.Current.SetTransactionComplete();
}
}
如果开发者指定服务的一个操作必须在一个事务的上下文中执行,那么WCF将确认服务配置了一个支持在消息里发送有关事务状态的绑定。提供了该支 持的预定义绑定包括:WSHttpBinding、WSDualHttpBinding、WSFederationBinding、 NetTcpBinding和NetNamedPipesBinding。最后两个绑定允许选择WS-AT协议或OleTx协议,而其他绑定都只使用标准 的WS-AT协议。
<bindings>
<netTcpBinding>
<binding name="..." transactionFlow="true" transactionProtocol="OleTransactions"/>
</netTcpBinding>
<wsHttpBinding>
<binding name="..." transactionFlow="true"/>
</wsHttpBinding>
</bindings>
WCF的客户端开发者可以使用System.Transaction命名空间提供的语法将服务的操作放进一个事务的范围内。
ServiceClient client =new ServiceClient("MyServiceEndpointConfiguration");
using(TransactionScope scope =new TransactionScope(TransactionScopeOption.RequiresNew))
{
client.DoSomething([...]);
scope.Complete();
}
client.Close();
如果服务的操作支持事务,且配置了支持传送有关事务状态的绑定,那么客户端的事务直到服务操作决定提交时才会提交。相反,如果客户端决定不提交事务,服务在客户端事务范围里对事务资源所做的任何操作都将被回滚。
但这样的设计并不太好,当远程客户端决定将采取何种动作的同时,服务的操作和其资源都会被一直占用。作为一个通用的原则,应该尽量避免使事务超出可信赖范围。
下图显示了一种更加巧妙地使用WCF实现分布式事务的方法。该方法融合了WCF对会话管理、队列交付和事务消息的支持。
图中的服务被配置成通过队列从客户端接收消息。客户端首先依次开始一个事务和会话,然后发送一组消息到服务。客户端完成会话,当且仅当客户端提 交该事务时,它发送的消息才会到达队列中。客户端发送的这些单独消息在队列里将会表示成单一条目。在服务端,WCF在队列中检测到这条消息后,会依次开始 一个事务和会话。然后它将单一消息重新分拆成客户端发送的一组单个消息,并将它们一一发送给服务。如果一切顺利,服务会关闭会话然后提交事务。如果在处理 任何消息的过程中出现任何差错,事务将会取消,也就是说,这种情况下,对事务资源中的任何消息,所做的任何处理都会被回滚。这组消息将被转移到有毒消息队 列中,对失败操作需要采取补救措施的服务可以从中读取消息。
这个设计的优点是,在客户端和服务端的操作不会被对方或它们之间的连接延迟所挂起。客户端和服务端的事务都是独立提交的。客户端的事务保证客户 端保持一致性状态,而服务端的事务保证服务一直处于一致性状态。如果客户端在它的事务范围内失败,服务端不会受影响,因为客户端所发送的消息作为事务的一 部分,不会被传送到服务端的队列中。如果服务端在处理客户发送的消息时失败,尽管客户端和服务端都处于一致性状态,但客户端和服务端的状态并没有相互一 致。在这种情况下,就需要采取一些措施来弥补这种不一致性状态
WCF揭秘学习笔记(4):可信赖会话、会话管理、队列、事务的更多相关文章
- WCF揭秘学习笔记(1):基础知识
最近找工作,面试时经常被问懂不懂WCF.不少招聘高级.NET工程师的要求上都 写着有WCF开发经验的优先考虑.我对于WCF仅仅是通过看一些教学视频这种山寨学习法了解一些.现在要下决心好好学习一下WCF ...
- WCF揭秘学习笔记(5):WF定制活动
WF(Windows Workflow Foundation,Windows工作流基础)为.NET提供了一种基于模型的.声明方式的过程执行引擎,它改变了传统的通过一行行编写代码来开发服务功能的方式. ...
- WCF揭秘学习笔记(3):使用DataContractSerializer
使用DataContractSerializer 终结点(包括地址.绑定.契约)可通过代码以编程方式添加到服务中.如: using(ServiceHost host =new ServiceHost( ...
- WCF揭秘学习笔记(2):数据表示
背景知识 WCF提供了一种语言为软件通信建模,称作服务模型.使用更底层的编程架构提供的类可以从这种语言建立的模型中生成可用的通信软件. 在服务模型使用的语言中,负责通信的软件部分称为服务(servic ...
- Spring MVC 学习笔记10 —— 实现简单的用户管理(4.3)用户登录显示全局异常信息
</pre>Spring MVC 学习笔记10 -- 实现简单的用户管理(4.3)用户登录--显示全局异常信息<p></p><p></p>& ...
- Spring MVC 学习笔记9 —— 实现简单的用户管理(4)用户登录显示局部异常信息
Spring MVC 学习笔记9 -- 实现简单的用户管理(4.2)用户登录--显示局部异常信息 第二部分:显示局部异常信息,而不是500错误页 1. 写一个方法,把UserException传进来. ...
- Spring MVC 学习笔记8 —— 实现简单的用户管理(4)用户登录
Spring MVC 学习笔记8 -- 实现简单的用户管理(4)用户登录 增删改查,login 1. login.jsp,写在外面,及跟WEB-INF同一级目录,如:ls Webcontent; &g ...
- GIT学习笔记(3):分支管理
GIT学习笔记(3):分支管理 何谓分支 GIT是如何存储数据的 GIT不是存储文件差异或者变化量,而是一系列文件的快照.在Git提交时,会保存一个提交(commit)对象,该对象包含一个指向暂存内容 ...
- kvm虚拟化学习笔记(四)之kvm虚拟机日常管理与配置
KVM虚拟化学习笔记系列文章列表----------------------------------------kvm虚拟化学习笔记(一)之kvm虚拟化环境安装http://koumm.blog.51 ...
随机推荐
- bzoj1626
题解: 简单最小生成树 x,y都要double 我也不知道为什么 代码: #include<bits/stdc++.h> using namespace std; ; int n,m,f[ ...
- 《Python》 文件操作
一.文件操作基本流程: 1.文件基本操作初识: 打开文件: 文件句柄 = open(‘文件路径’,‘编码方式’,‘打开方式’) 第一种:f = open('d:\'a.txt',encoding='u ...
- DevExpress v17.2新版亮点—ASP.NET篇(三)
用户界面套包DevExpress v17.2终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress ASP.NET v17.2 的GridView Control. ...
- MyEclipse移动开发教程:设置所需配置的iOS应用(三)
MyEclipse个人授权 折扣低至冰点!立即开抢>> [MyEclipse最新版下载] 三.创建配置文件 Provisioning profiles授权文件应用程序在iOS设备上安装并运 ...
- main方法原来只要放在public static类中就能跑,涨知识了
接口中可以装在嵌套类对象. public interface ClassInterface { void howdy(); class Test implements ClassInterface { ...
- Latex的\cite后面的参考文献显示问号
今天编写Latex的参考文献,发现编译之后参考文献都是问号,很疑惑.网上搜到一个帖子,发现他的问题和我的类似,但他比我还多出一个问题,就是Bibtex按钮是灰色的无法使用. 遇到“看不到Bibtex按 ...
- IDC:2014年的十大 IT 趋势
IDC:2014年的十大 IT 趋势 市场研究公司 IDC 近日发布报告,对 2014 年的十大科技行业发展趋势作出了预测.IDC 称,2014 年将是科技业"鏖战正酣"的一年,整 ...
- XCODE中使用Main.Storyboard拉入控件并实现事件(Swift语言)
如何在XCODE中的Main.Storyboard内拉入控件并实现一个简单的效果呢?本人由于刚接触Swift语言不久,对于IDE的操作还是很生疏,不懂了就在网上参考了网上前辈们的文章.以下我将演示如何 ...
- 【转】WinForm基础
winform基础 先修基础:C#语法基础和面向对象编程 1.Winform创建解决方案 2.Winform窗体 3.MessageBOx 4.Winform登录.控制软件只运行一次.回车登录 5.W ...
- IDEA创建的Web项目配置Tomcat并启动Maven项目
点击如图所示的地方,进行添加Tomcat配置页面 弹出页面后,按照如图顺序找到,点击+号 tomcat Service -> Local 注意,这里不要选错了哦,还有一个TomE ...