几天前,有朋友在QA上问有关WCF中可信赖会话(ReliableSession)的问题。今天重新梳理一下,主要目就是真正的弄清楚可信赖会话的作用,方法和现实意义。
正如那位的疑惑一样,很多做过WCF开发的朋友,也都对ReliableSession不太理解。查阅msdn,可以发现ReliableSession是System.ServiceModel命名空间下的一个类。主要的作用如下所述:
提供对可靠会话绑定元素属性的便捷访问,这些属性是在使用系统提供的绑定之一时提供的。 |
包括两个重要的属性(Property)成员:
1.InActivityTimeout
文档中的说明是:获取或设置服务在关闭之前保持非活动状态的时间间隔。
说白了一点呢,就是如果服务停留多长时间没有工作,那我们就关闭他(嘿嘿,有点像公司对待员工一样,偶 尔休息一下子,不干活还行,时间长了,老板会炒你鱿鱼的哦!)
2.Ordered
文档中说明为:获取或设置一个值,该值指示消息传递是否必须保持与消息发送一致的顺序。
任何一方,无论是客户端发送请求消息(Request Message),还是服务端发送相应消息(Response Message)。发送出来的消息都有先后次序,而接收方在接收这些消息的时候,有时候会出现错位的现象,有的消息可能后发先至,而有的可能先发后至,这就影响了通讯双方正常的交互。产生这样现象的主要的原因有两点:
1)未使用可靠连接协议,使得在传输层中出现类似现象
2)虽然试用了可靠连接协议,但在发送端或者接收端的通道中(Channel)发送或者接收有顺序问题
tcp协议用于解决第一个问题,而ReliabelSession的Ordered正可以解决第二个问题。
在WCF的缺省绑定中,只有以下几种支持或者默认打开了ReliableSession的功能,他们为:
wsHttpBinding
wsDualHttpBinding
wsFederationHttpBinding
netTcpBinding
其中netTcpBinding,wsHttpBinding,wsFederationHttpBinding中的ReliableSession属性均是RelibaleSession的一个派生类型,名为OptionRelibaleSession,也就是提供了可选的功能。相比ReliableSession,OptionReliableSession仅仅多了一个属性:Enabled,用这个属性可以指示是否开启可信会话。true代表开启,false的话就不开启了,那么对InActivityTimeout和Ordered的设置将无效。这里面wsHttpBinding和wsFederationHttpBinding其实都继承自wsHttpBindingBase,所在在讨论ReliableSession的时候,这两个Binding表现一致。而对与wsDualHttpBinding则默认的将ReliableSession属性的Ordered设置为了true.也就是说wsDualHttpBinding是缺省支持可信会话的。而ReliableSession的InActivityTimeout默认情况是是10分钟。
在上面对ReliableSession的描述中,我们可以清楚的看到便捷描述的字样。这是怎么一回事情呢?原来ReliableSession这个class并没有缺省的构造函数,而只是有一个接收ReliableSessionBindingElement类型的构造,这就引出了相比更重要的角色:ReliableSessionBindingElement. 下面就来说说它,并说说它和ReliableSession之间,和Binding之间有着怎样的联系
ReliableSessionBindingElement:
BindingElement的一个派生类,也就是说它是一个绑定元素。熟悉WCF架构或者读过大侠Artech<<WCF后续之旅>>的朋友,应该知道WCF的Foundation是如何的。不知道的也没有关系,我也来说说大概,WCF暴露给外界的是终结点(EndPoint),在WCF基础部门,一个终结点又有主要的三部分组成:1)地址 (Address) 2) 绑定Binding 3) 契约 (Contract),也就是传说中的"ABC",呵呵。地址相对比较简单,契约是用于WCF框架留给开发人员创建具体业务逻辑代码的地方,而WCF中,最核心,也是最具有魅力的地方就在于Binding.对于Bind的认识可以帮助我们更清楚的解析WCF的一些特征。一个Binding会维护一个BindElement的集合,这个在Binding的派生类CustomBinding中作为Elements属性来表示,Elements是一个Collection<BindingElement>的类型。在Elements中的每个BindingElement都有着各自的功能,比如在NetTcpBinding中,就存在这这样的一种BindingElement组织方法
处于第一个位置的是TransactionFlowBindingElement,如果支持ReliableSession的话,那么ReliabelSessionElement就处于NetTcpBinding中的第二个位置 ,如果不支持ReliableSession,则此元素将存在,紧接着的是SecurityBindingElement,随后是BinaryMessageEncodingBindingElement用于消息编码,随后是用于通讯安全的Element,它的类型由NetTcpSecurity属性来决定。最后是TcpTransportBindingElement.而且需要注意的是,每一个Binding中,最后一个Element必须都为TransportBindingElement。在上面的所谈论到的BindingElement中,各自都有各自的功能,他们各司其职,Binding只是将他们紧密的组织在了一起。但每个BindingElement是不是真正的执行者呢?NO!他也是一个组织者,他组织的对象主要有两个:
ChannelFactory
ChannelListener
其中ChannelFactory是作为客户端使用的,而ChannelListener是作为服务端时候使用的。不幸的是,无论是ChannelFactory还是ChannelListener,也都不是真正的执行者,他们是IChannel的工厂类。真正在里面辛勤劳动的还是实现了IChannel的Channel对象。至于说ChannelFactory和ChannelListener是如何组织Channel的,和Channel的一些具体行为,篇幅有限,不再赘述。为了给大家一个详细的认识,画了几幅图:
接下来,我们分别放大终结点,将终结点的详细构造用图形表达出来:
BindingElement管理两个对象ChannelFactory和ChannelListener
这一级的表示图如下:
上面说了这么多有关WCF基础架构的(Infrastructor)的,下面返回再来看ReliableSessionElement,首先我们就会从宏观上理解它了,它就是一个类型的BindingElement,它包含如下的主要属性成员:
InActivityTimeout
Ordered
MaxRetryCount
上面的几个重要属性中,InActivityTimeout和Ordered两个属性和ReliabelSession的作用是一致的。而MaxRetryCount用于如果指定了使用可信通道,也就是Ordered为True的时候,当可信通道没有收到消息的时候,通过在基础通道上调用Send方法重发此消息的次数
ReliabelSession是ReliableSession的一种快捷表示,原因是在构造Binding的时候的,会首先构造一个ReliableSessionElement对象,然后将这个对象的引用传递给ReliableSession对象,这样其实ReliableSession对象其实就是ReliableSessionElement对象中的一部分数据,只是由于暴露ReliabelSessionElement给开发人员,有些不优雅,所以单独设计了这么一个ReliableSession的类,用于快捷表示ReliableSessionElement.
好了,从理论上讲,我所认识的ReliableSession说的差不多了。下面借鉴一个有趣的实例来检验一下ReliableSession到底有何作用。使用它和不使用它有何异同。
这个试验的构思来源于WindowsCommunicationFoundation Unleashed<<WCF揭秘>>一书。传说中,这个示例的构思来自于WCF项目经理Shy Cohen.他的思路是将1张图片分成100分,每一份作为一个消息发送给接受者,接受者在根据接收到的消息产生绘制图像。而在发送的过程中,通过插入一个自定义的BinddingElement,将数据分概率的截取下来不予发送。这时候,如果不采用ReliableSession,那么接收方收到的图像是不完整的。有一小部分缺失,而如果采用了ReliableSession呢,那尽管发送的时候,在被拦截的时候的当此发送中,消息的确没有发出去,但基础通道会尝试进行重发,重发的包有很小几率会再次被拦截,如果再次被拦截,那么会再次重发,那么被拦截的几率会更小,这时候接受者收到的是完整的图像。
在Shy Cohen的试验中,由三个组件构成:
发送者(Sender)
接受者 (Receiver)
对照者 (Router)
他们之间的关系如图:
对照者能够指定发送者漏包的比率,比如1%,10%等,发送者与对照者之间一直使用的是没有可靠连接的会话,试验的步骤是:
1)首先将接受者和发送者都设置为不接受可信赖会话。设置的方法就是将各自config中的添加<reliableSession Ordered="false"/>。此时,运行三个程序,在对照者中调整丢包频率,在发送者中选择一幅图片,注意:最好是它例子中给定的Seattle.jpg,因为其他图片可能因为过大,而造成异常。选择图片之后,点击发送。此时,在对照者和接受者中会显示出接收到的包数目,丢失的数目,和发送的数目。如果设置的比率比较大的话,会发现接受者所形成的图像有明显缺失。对照者中的丢包数目也大于0
通过这一步骤,可以得出结论,如果没有采用可信会话,消息在发送的过程中是有可能丢失或者错位的,尽管我们使用的是Tcp协议
2)将接受者和发送者config中的配置更改为<reliableSession Ordered="true"/>,此时,再次选择图片,发送,会发现在偶尔的几次中,发送操作进行的非常迟钝,但发送完成之后,接受者的图像是完整的,而对照者仍然存在着一些丢失的消息。
通过这个试验,能清晰的表达可信会话的功能和意义。我这里这是做了一个介绍,更多更详细的请阅读<<WCF揭秘>>第四章。