【踩坑(Running)填坑(ZSSURE)】:WCF学习之InstanceContextMode与ConcurrencyMode

背景:

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原创文章,转载请注明出处,未经允许不得转载。

时间: 2024-10-17 00:04:28

【踩坑(Running)填坑(ZSSURE)】:WCF学习之InstanceContextMode与ConcurrencyMode的相关文章

移动前端:坑与填坑

1.页面高度渲染错误 坑:页面底部部分与浏览器导航条重合了 填坑:重置高度 document.documentElement.style.height = window.innerHeight + 'px'; 2.transform碰上模糊 坑:在android中,如果元素或其父元素应用transform后,元素设置border-radius会变模糊 填坑:先放大再缩小 body{padding: 20px;background:purple;-webkit-transform: transla

纯Socket(BIO)长链接编程的常见的坑和填坑套路

本文章纯属个人经验总结,伪代码也是写文章的时候顺便白板编码的,可能有逻辑问题,请帮忙指正,谢谢. Internet(全球互联网)是无数台机器基于TCP/IP协议族相互通信产生的.TCP/IP协议族分了四层实现,链路层.网络层.传输层.应用层. 与我们应用开发者接触最多的应该是应用层了,例如web应用普遍使用HTTP协议,HTTP协议帮助我们开发者做了非常多的事情,通过HTTP足以完成大部分的通信工作了,但是有时候会有一些特殊的场景出现,使得HTTP协议并不能得心应手的完成工作,这个时候就需要我们

NHiberante从.net framework转移到.net standard(.net core 2.2)时遇到的坑及填坑

在.net framework中的创建session代码先贴一个 1 public class SessionBuilder 2 { 3 private static ISessionFactory _sessionFactory = null; 4 5 public SessionBuilder() 6 { 7 if (_sessionFactory == null) 8 { 9 //创建ISessionFactory 10 _sessionFactory = GetSessionFactor

开坑,填坑——莫比乌斯反演

hdu 1695 题目:给出x和y的范围,要求gcd(x,y)==k的数对个数. 思路:首先把范围除k,然后就是求gcd(x,y)=1的数对个数.具体莫比乌斯公式的用法还不是很懂,目前的理解是这样的: 莫比乌斯公式给出了一个从和函数反演到原函数的方法.对于一个定义在正整数上的函数,其和函数F(n)定义为所有f(d)|d是n的因子的和.然后根据莫比乌斯公式,可由F求得f. 但是此题的形式有点不同.设f(k)表示gcd(x,y)=k的数对的个数.然后设F(k)表示gcd(x,y)=k的倍数的数对的个

挨踢部落故事汇(32): Java深坑如何填?

世上本没有坑,踩的人多了也便成了坑.每遇到一次困难,每踩一个坑,对程序员来说都是一笔财富.持续学习是程序员保持竞争力的源泉.本期将分享一个踩坑无数的Java程序猿填坑秘籍. 榆木,一个阅历无数(踩坑)的技术宅男,喜欢了解新技术却不爱太钻研新技术(因为懒,猿届反面角色一枚).14年毕业至今,在Java开发这条道路上可谓是坑过好些人.也埋过好些坑.也被坑过好些次.因为懒,没有针对他遇到过的问题做过太多的笔记(记录一些棘手问题的解决方法还是个不错的习惯),只是习惯性的去分析为什么出现这样的问题,我们该

【填坑纪事】一次用System.nanoTime()填坑System.currentTimeMills()的实例记录

JDK提供了两个方法,System.currentTimeMillis()和System.nanoTime(),这两个方法都可以用来获取表征当前时间的数值.但是如果不仔细辨别这两个方法的差别和联系,在使用当中也很容易出错.笔者在前不久的工作当中使用System.currentTimeMillis()时就踩了一个大坑,后来在查明System.currentTimeMillis()和System.nanoTime()的特性后,才用System.nanoTime()来填了这个坑.本文,笔者就以自己的踩

支付宝和微信支付的各种填坑

填坑 支付宝填坑是每个接入支付宝必经之路,下面是我接入支付宝遇到的问题汇总,希望大家在接入的路上少一点弯路 问题1. Util/base64.h:63:21: Cannot find interface declaration for ‘NSObject’, superclass of ‘Base64’ 解决办法: 这是base64.h中没有加入#import 系统库文件导致,这个错误报错方法直接想喷它一脸.报错方式太恶心. 1 2 解决办法: 这是base64.h中没有加入#import  系

10分钟搞定支付宝和微信支付 的 各种填坑

填坑   支付宝填坑是每个接入支付宝必经之路,下面是我接入支付宝遇到的问题汇总,希望大家在接入的路上少一点弯路 问题1. Util/base64.h:63:21: Cannot find interface declaration for ‘NSObject’, superclass of ‘Base64’ 解决办法: 这是base64.h中没有加入#import  系统库文件导致,这个错误报错方法直接想喷它一脸.报错方式太恶心. 问题2.截图告知你什么问题 解决办法: 这个问题可以同上的,心情

Cython的用法以及填坑姿势

因为项目需要,需要优化已有的Python代码.目前Python代码的执行过程是将Python代码转变成一行行指令,然后解释器解释指令的执行,调用到C代码层.如果去掉指令解释这个阶段,直接进入C代码层,效率就比较高了.如果用之前所述的使用Python C API将Python代码改造为C代码并作为Python的内建模块,工作量极其大,也不能保证其正确性,所以这种方法不太现实.而Cython库正好符合这种场景需求,将已有的Python代码转化为C语言的代码,并作为Python的built-in模块扩