背景:
WCF可以看作是微软对SOA架构的一种实现,或者说WCF的存在让开发者更容易创建面向服务的程序。面向服务本身不是一种技术,而是设计和实现软件的一种架构方式。从最早的面向过程(PO)、面向对象编程(OO),到后来的面向服务(SO)、面向资源(RO)编程,本身没有本质区别,反映出的是人们认识世界的方法论的迭代进化。
PO、OO、SO、RO,还有一个不沾边的O2O,各种概念层出不穷,在学习使用时要切忌混淆。今天这里记录的是在WCF框架下(面向服务架构——SOA——的一种实现),如何进行多线程及并发管理。多线程、并发、加锁、生命周期,这些知识都是老生常谈,剖开新框架的外衣,其本质是没有变化的。
WCF概念介绍:
在进入正题之前,先介绍WCF框架中几个主要概念。
1.EndPoint
WCF通过终结点EndPoint来发布服务,终结点EndPoint包括ABC三要素:
这里可以用常见的互联网服务来简单对比一下,Address就是我们使用的URL、Binding就是HTTP或HTTPS协议,Contract就是GET、POST等各种方法。互联网服务端最最常见的就是Apache Httpd、Apache Tomcat、JBoss等各种容器托管服务软件,由于HTTP协议是无状态协议,因此我可以简单的理解为每一次、每一个用户请求服务端都会创建单独的、新的一个响应对象(例如Servlet对象)来处理用户请求,这就跟我们下面要讲到的实例(Instance)和会话(Session)有关。
2.Instance vs Session
看一下实例(Instance)与会话(Session)两者的对比,
概念 | 含义 | 备注 |
---|---|---|
实例(Instance) | WCF中的实例管理旨在解决服务实例的激活和服务实例生命周期的控制 | 实例上下文是对服务实例的封装,是WCF管理服务实例的生命周期的依托。 |
会话(Session) | 会话的目的在于保持相同客户端(服务代理)多次服务调用的状态 会话通过消息识别机制判断调用某个服务的消息来源,从而将来自于同一个客户端的消息关联在一起 |
【备注】:关于生命周期最容易想到的就是Java或C#中的垃圾回收,而这两种主流商用语言都采用了“跟搜索算法”,详情参见《深入理解Java虚拟机:JVM高级特性与最佳实践》
WCF实例模式与并发模式的对比
高性能(Performance)和高伸缩性(Scalability),是软件设计与架构中永远不可以同时兼顾的,原因很简单,高性能往往需要充足的资源,高扩展性又需要尽可能的节约资源。所以我们才说,软件设计与架构是一项“权衡”的艺术。
虽然在前文中对比了会话与实例的概念,但是具体应用中会话与实例往往是分不开的,会话过程中必然用到实例来进行请求处理。通过对比WCF中实例上下文模式与并发模式,以及最后给出具体的示例程序,来了解一下WCF是如何兼顾高性能和高伸缩性的。
1. 实例上下文模式:InstanceContextMode
WCF的实例上下文模式InstanceContextMode分为以下三种,
实例上下文 | 说明 |
---|---|
单调模式(Per-Call) | 决定于单调实例上下文模式,即InstanceContextMode |
单例模式(Single) | 决定于单例实例上下文模式,即InstanceContextMode |
会话模式(Per-Session) | 取决于会话实例上下文模式、会话信道、会话信道栈,即有会话模式、绑定、实例上下文模式的三者决定,如下图所示: |
服务实例的创建过程,其中会使用反射这样相对影响性能的操作,但是在WCF应用中,真正影响性能的是操作时信道的创建和释放。服务实例的激活和他们比起来,可以说微不足道。如果在应用中出现对基于相同服务代理的频繁调用,比如服务调用放在一个for循环中调用上百次,服务实例的创建带来的性能损失就不能不考虑了。
2. 并发模式:ConcurrencyMode
注:截图来自《WCF全面解析(下册)》,作者蒋金楠
WCF实例模式与并发模式的对比实例测试
1. 设置InstanceContextMode=Single,ConcurrencyMode=Single
由上图可以看出,在WCF服务包含回调并且InstanceContextMode与ConcurrencyMode同时设置为Single后,会弹出死锁问题,该问题原因在于服务端的InstanceContext实例在响应客户端请求后会发起回调请求,而此时客户端恰恰在等待服务端的返回结果才能启动回调函数,因此导致两者死锁。解决方案如下。
2. 设置InstanceContextMode=Single,ConcurrencyMode=Reentrant
客户端与服务端显示结果如下,
由上图可以看出InstanceContextMode标明的是WCF创建InstanceContext的个数,此处设置为Single指的是所有客户端的请求都由同一个InstanceContext来执行,虽然客户端使用了ThreadPool.QueueUserWorkItem发起并发请求,但是在服务端的实际运行过程中是串行执行的,为了更好的观察执行流程,绘制执行时间简图如下:
【注意】:为何Reentrant能够解决死锁问题,可以看考一下《WCF全面解析(下册)》,截图如下:
【zssure】:有上述可知,Single对实例上下文的加锁是完整的,直到此次服务请求完成(包括请求、回调、回调响应、回调后操作);而Reentrant在发起回调后会释放锁,当然此时如果由其他的客户端使用了该实例上下文,回调响应需要等到其他客户端释放该实例上下文后方可运行,此时如果超时就会抛出异常。上图自己绘制的时间图中将服务端的InstanceContext分化成了两个实例,一个用于正常请求,一个用于回调,其目的是为了更清晰的标明整个响应流程。至于WCF底层是如何来实现同时响应新的客户端请求以及发起回调请求的有待进一步核实。
3. 设置InstanceContextMode=Single,ConcurrencyMode=Multiple
正如《WCF全面解析(下册)》书中所言,将ConcurrencyMode设置为Multiple后,服务端的InstanceContext实例可以同时并发响应多个客户端的请求,实现真正意义上的并发。
4. 设置InstanceContextMode=PerCall,ConcurrencyMode=Multiple
再次将InstanceContextMode设置为PerCall查看一下运行结果,如下所示:
由此可以看出与第3中情况是相同的,但是此时如果在控制台中间InstanceContext具体实例的HashCode打印出来会发现,三次客户端请求会在服务端创建三个InstanceContext实例,这是与上面第3中情况不同的地方。
总结
由此可以看出InstanceContextMode主要控制的是服务端如何创建具体的实例对象,而ConcurrencyMode控制的是实例与客户端请求之间的分发方式。如下图所示:
在WCF中通过实例上下文模式InstanceContextMode的设置来确定服务响应时如何创建具体的操作实例(Instance),另外通过并发模式ConcurrencyMode来控制客户端请求与具体操作实例之间的分发和响应机制。——而这整个过程都可以简单归属为会话层。
作者:[email protected]
时间:2015-11-19
版权声明:本文为zssure原创文章,转载请注明出处,未经允许不得转载。