WCF是微软在SOA (面向服务)领域的一款利器,先不谈WCF,我们来聊聊SOA。SOA 全称是Service Oriented Application, 也是由面向过程(汇编,C),面向对象(C++, C#, java),面向组件,面向内容…发展过来的。
面向过程可以简化为函数,也可以说我们首先把问题抽象为有限个步骤,这些有限个步骤可以经过计算得到输出结果;
面向对象则首先把我们要处理的事物以一种抽象的层次引入,与面向过程相比多了属性,以及更高层次的集合,简而言之就是先通过抽象剥离,再通过集合汇聚,然后形成一个可以在计算机内表示具体事物的结构;
面向组件是要解决面向对象中不同对象间的依赖关系导致牵一发而动全身的状况而提出的。组件比起对象来的进步就在于通用的规范的引入。通用规范往往能够为组件添加新的能力但也给组件添加了限制,组件一般用于特定平台。
面向服务这里要多费些笔墨,我们可以环顾四周,我们生活的环境,社会,无时无刻,无处不在的面向服务的例子。我们的衣食住行,生老病死都与社会离不开,也都与服务离不开。社会就是最大的服务载体,我们在消费服务(衣食住行,生老病死)的同时也在提供服务(360行,软件开发人员为公司提供产品服务)。可以说面向服务实现了与现实的完美结合。
既然是面向服务,那么如何定义服务,如何提供服务,如何寻找服务,如何使用服务呢? WCF 主要关注如何如何提供服务,如何使用服务,而服务一般都不在本地,但你可以知道服务的地址(WCF),也可以不知道服务的地址(云),而WCF就是为了让分布式服务对开发人员就像本地服务一样,负责处理了通信,编码,安全,可信赖,事务等具体事宜。通信,编码,安全和事务不是我们本篇要讨论的内容,这里我们主要讨论一下可信赖消息。
下面进入正题,WCF 的可信赖信息的主要内容体现在可信赖会话,Reliable Session. Reliable Session 是一种绑定行为,主要包括三种属性Enabled, InactivityTimeout, Ordered.
Enabled: 启用可信赖会话;
InactivityTimeout:会话空闲的超时时间,就是客户端与服务端建立起的会话保持Idle状态多长时间才会超时;
Ordered:是否按顺序发送消息,网络上发送消息很有可能会乱序,这个选项用来保证消息是按顺序发送发送到目的地。
下表列出了WCF中各种不同的绑定对可信赖会话的支持以及其他属性。
表1-1 绑定特性
这里为了讲述WCF可信赖会话方便,我们引入一个现实问题。
问题的提出: 在PerSession绑定模式下,有多个客户端调用同一个寄宿服务,服务端如何实现在没有客户端调用且持续一段时间后自动关闭服务?
问题的分析: 关于PerSession请参考这篇文章。我们知道如何使用单例模式,即所有客户端调用在服务端都只生成一个服务实例,那么客户端就可以共享服务实例上下文,从而解决问题。PerSession简单地说就是服务端为每个客户端请求生成一个服务实例,这些服务实例不可互相访问。如果我们使用一种方法让它们实现共享,不就可以解决问题了吗?
以下是我们想到的几种解决方法:
1. 在服务端维护一个客户端调用服务的列表,然后记录它们的空闲时间。
2. 在服务端将客户端信息记录到文件系统中,然后实现消息共享。
上面第一种解决方案在实现上是可行的,但是未免有很多复杂,第二种解决方案不推荐使用。
现在我们考虑另外一种方案,也就是使用可信赖会话中的InactivityTimeout,通过它我们可以解决上述问题。
由InactivityTimeout想到的其他问题: 在此问题中我们使用了PerSession, 也就是客户端与服务实例是1:1 的关系,那么假设我们开启了一个客户端,那么服务端对应就会生成一个个服务实例,假设服务端的InactivityTimeout值是50秒,而客户端的InactivityTimeout值是30秒,那么会出现什么情况呢?
另外,如果我们开启了两个客户端,它们一个是在第20秒的时候开启,另一个是在第30秒的时候开启,服务端的InactivityTimeout值是50秒,又会出现什么情况呢?
具体结果你可以通过例子测试,我们这里直接给出测试结果:
第一种情况: 到1分钟后客户端抛出异常,这是因为50秒后服务端已经释放服务实例,这个时候客户端要关闭服务实例时已经无法正常调用,导致Close 方法阻塞,又因为默认配置下closeTimeout=”00:01:00”,所以到1分钟后客户端抛出异常,这实际上是由服务端的InactivityTimeout=“50” 间接导致的。
第二种情况: 两个客户端在分别等到自己的closeTimeout 时间到了以后抛出异常。
由此可见,InactivityTimeout 在服务端和客户端都有配置,服务端InactivityTimeout超时以后会关闭服务实例,而不是ServiceHost,客户端抛出异常的时间默认情况下取决于客户端的closeTimeout时间,而非InactivityTimeout配置时间。
附件内容: MSDN
When using a reliable session, there are two different inactivity timers that must be satisfied to keep the connection alive. If either of these inactivity timers goes off, then the connection is dropped.
-
The first inactivity timer is on the reliable session and is called the InactivityTimeout. This inactivity timer fires if no messages, either application or infrastructure, are received within the timeout period. An infrastructure message is a message that is generated for the purpose of one of the protocols in the channel stack, such as a keep alive or an acknowledgment, rather than containing application data.
-
The second inactivity timer is on the service and uses the ReceiveTimeout setting of the binding. This inactivity timer fires if no application messages are received within the timeout period.
Since the connection is dropped if either inactivity timer fires, increasing InactivityTimeout once it is greater than ReceiveTimeout has no effect. The default for both of these timeouts is 10 minutes, so you always have to increase both of them to make a difference when using a reliable session.
翻译:
当使用一个可信赖会话时,必须同时满足两个不同的空闲计数器才可以保持连接alive.
如果任何一个空闲计数器超时,那么连接就会断开。
第一个空闲计数器是在可信赖会话上,被称为InactivityTimeout。这个空闲计数器会在应用程序或基础架构在空闲一定时候且没有消息的时候触发。一条基础架构消息是一条为在信道栈中的一个协议生成的消息,比如一条keep alive或者一个回复消息,不包含应用数据。
第二个空闲计数器是在服务上的,使用绑定的ReceiveTimeout设置。这个空闲计数器会在没有应用程序消息被接受到且持续一定时间后触发。
既然连接会在任何一个空闲计数器触发时断开,增加InactivityTimeout 值使其大于ReceiveTimeout是没有作用的。这两个超时的默认时间都是10分钟,所有你总是不得不同时增加它们的值来保证可信赖会话的不同。