上一篇:WCF把书读薄(1)——终结点与服务寄宿

  八、消息交换模式

  WCF服务的实现是基于消息交换的,消息交换模式一共有三种:请求回复模式、单向模式与双工模式。

  请求回复模式很好理解,比如int Add(int num1, int num2)这种方法定义就是典型的请求回复模式,请求者发送两个数字,服务回复一个结果数字。如果采用ref或者out参数,那么在xsd当中,ref参数会作为输入和输出参数,out参数只作为输出参数。在WCF当中void返回值的操作契约其实也是请求响应模式的,因为将返回值改为void,影响的只是回复消息的xsd结构,void返回的是一个空xml元素(P141)。

  对于一些调用服务记录日志等不要求有响应(即便抛异常也不需要客户端知道)的行为,应该采用单向模式,单向模式只需要在操作契约上添加单向的属性:

[OperationContract(IsOneWay=true]
void WriteLog(string msg);

  单向模式的操作在对应的wsdl当中没有输出节点,这样的操作必须使用void作为返回值,其参数也不能够使用ref和out参数(P144)。

  最后一类是双工模式,双工模式是在服务端定义接口,由客户端实现这个方法,服务端“回调”客户端的这个方法。这里直接扒书加法的例子,因为这个例子又简单又能说明问题,这个例子当中客户端调用服务端的加法,服务端回调客户端的显示函数。

  首先定义服务契约:

[ServiceContract(Namespace = "http://www.artech.com/", CallbackContract = typeof(ICalculatorCallback))]
public interface ICalculator
{
[OperationContract(IsOneWay = true)]
void Add(double x, double y);
}

这里定义了CallbackContract属性,需要传入一个接口的名字,这个接口名字就是回调操作契约,既然在这里指明了它是个契约,就无需服务契约标签了,这里之所以采用单向,是为了防止死锁:

public interface ICalculatorCallback
{
[OperationContract(IsOneWay = true)]
void DisplayResult(double result, double x, double y);
}

契约实现如下:

public class CalculatorService : ICalculator
{
public void Add(double x, double y)
{
double result = x + y;
ICalculatorCallback callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>();
callback.DisplayResult(result, x, y);
}
}

注意实现的第二行,先从当前操作上下文当中拿到了回调信道,之后调用它的回调方法。

客户端实现如下:

public class CalculatorService : ICalculator
{
public void Add(double x, double y)
{
double result = x + y;
ICalculatorCallback callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>();
callback.DisplayResult(result, x, y);
}
}

首先是一个回调函数的实现类,它实现了回调契约,不过老A的例子有些不雅,这里直接引了契约的dll。

然后是客户端的主体:

class Program
{
static void Main(string[] args)
{
InstanceContext callback = new InstanceContext(new CalculatorCallbackService());
using (DuplexChannelFactory<ICalculator> channelFactory = new DuplexChannelFactory<ICalculator>(callback, "calculatorservice"))
{
ICalculator calculator = channelFactory.CreateChannel();
calculator.Add(, );
}
Console.Read();
}
}

这里首先创建了实例上下文,用它和终结点的配置一起创建了双工信道工厂,之后通过这个工厂创建信道来实现双工调用(这里不雅同上)。

  服务端的配置如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="exposeExceptionDetail">
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Artech.WcfServices.Service.CalculatorService"
behaviorConfiguration="exposeExceptionDetail">
<endpoint address="http://127.0.0.1:3721/calculatorservice"
binding="wsDualHttpBinding"
contract="Artech.WcfServices.Service.Interface.ICalculator"/>
</service>
</services>
</system.serviceModel>
</configuration>

这里采用了支持双工通信的wsDualHttpBinding绑定,客户端配置如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint name ="calculatorservice"
address="http://127.0.0.1:3721/calculatorservice"
binding="wsDualHttpBinding"
contract="Artech.WcfServices.Service.Interface.ICalculator"/>
</client>
</system.serviceModel>
</configuration>

  九、实例与会话

  上面了例子里有一个InstanceContext对象, 这个对象就是实例上下文,它是对服务实例的封装,对于一个调用服务的请求,WCF会首先反射服务类型来创建服务实例,并用实例上下文对其进行封装(当然这个实例是带“缓存”的),我们可以配置一定的规则来释放上下文(P396)。

  实例上下文分为三种模式:单调模式、会话模式和单例模式。上下文的模式是服务的行为,与客户端无关,以[ServiceBehavior]的InstanceContextMode属性来设置。下面分别来看一看这三种模式。

  单调模式,表示每一次调用服务都会创建一个全新的服务实例和上下文,上下文的生命周期与服务调用本身绑定在一起(P402),这种方式能最大限度地发挥资源利用率,避免了资源的闲置和竞争,因此单调模式适合处理大量并发的客户端(P406)。

  实现单调模式需要在服务的实现类上增加反射标记:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class CalculatorService : ICalculator

  从这里也能看出,服务的实现类并不代表业务逻辑,而是位于业务逻辑之上的一个“隔离层”,它显然属于服务层。

  单例模式则走了另一个极端,这种模式让整个服务器上自始至终只存在一个上下文,它的反射标签是:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]

  既然只有一个上下文,那么说明同时只能处理一个请求,剩下的请求去排队或者超时。这种模式只能应付很少的客户端,而且仅限于做全局计数这样的操作。如果需要让这个服务异步执行,需要这样写反射标签:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Multiple)]

  会话模式则将为每一个服务代理生成一个上下文,会话使服务具有识别客户端的能力,所以一定要选用支持会话的绑定(P420),这种模式适合于客户端数量很少的应用。

  这种模式的服务契约上面有SessionMode标签,Required对服务的整个调用必须是一个会话,默认值为Allowed,会在适当时机采用会话模式。服务契约含有IsInitiating和IsTerminating两个属性,在客户端调用服务时,必须先调用IsInitiating为true和IsTerminating为false的,作为起始,最终要调用IsInitiating为false而IsTerminating为true的,作为终结,在两者之间可以调用全为false的操作。如果不这样调用会报错。

[ServiceContract(SessionMode=SessionMode.Required)]
public interface ICalculator
{
[OperationContract(IsInitiating=true, IsTerminating=false)]
void Reset();
[OperationContract(IsInitiating = false, IsTerminating = false)]
void Add(int num);
[OperationContract(IsInitiating = false, IsTerminating = true)]
int GetResult();
}

  服务实现如下,首先服务行为加上了InstanceContextMode=InstanceContextMode.PerSession,并在服务的内部保存了一个叫做result的非静态变量:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class CalculatorService : ICalculator
{
private int result;
public void Reset()
{
result = ;
} public void Add(int num)
{
result += num;
} public int GetResult()
{
return result;
}
}

  上面一共提到了InstanceContextMode和SessionMode两个枚举,当采用PerCall单调服务时,不论SessionMode如何,中间结果都不会被保存;采取Single单例服务时,不论SessionMode如何中间结果都会被保存,因为上下文是单例的;采取PerSession会话服务时,只有会话模式为Required和Allowed时,中间结果才会被保存。(P427)一张图说明问题:

  

  

  十、并发

  服务行为的InstanceContextMode表示的是对于一个请求,在服务端搞出几个实例上下文来,那么,ConcurrencyMode则表示同一个服务实例如何同时处理多个并行到来的请求,这些请求可能来自同一个服务代理的并行调用,也可能来自多个服务代理的同时调用。

  不过在使用ConcurrencyMode之前,需要先给服务/回调服务加上如下标记:

[ServiceBehavior(UseSynchronizationContext=false)]

[CallbackBehavior(UseSynchronizationContext=false)]

  这是因为服务操作会自动绑定服务的寄宿线程,为了打破这种线程的亲和性需要禁用同步上下文,否则服务就将是串行执行的,并且是采用同一个线程执行的,就没有什么“并发”可言了。(下P197)

  对于并发模式,WCF同样提供了三个可选模式。

  Single模式表示一个实例上下文在某时刻只能处理单一请求,也就是说针对某个服务上下文的并发请求会串行执行。

  在这种模式下,当并发请求到来时,WCF会对实力上下文进行上锁。

  Multiple模式表示一个实力上下文可以同时处理多个请求。

  Reentrant(可重入)模式和Single类似,只能同时处理一个请求,然而一旦这个请求处理着一半就去回调客户端了,那么在客户端响应之前,其他的并行请求还是可以被它处理的。举个不雅的例子,男人和老婆亲热着一半,老婆出去拿东西了,这时在外排队的小三就可以进来,等老婆回来了,需要先等小三出来,自己再进去……

  在这种模式下,如果需要服务端对客户端进行回调,那么要么采用OneWay的形式回调,要么就要把服务的并发模式设置为非Single,否则会造成死锁的异常,因为“小三”是会占有“原配”的锁的。(下P182)

  要让服务支持并发,需要给服务打上服务行为标签,默认值是Single,同样也可以给CallbackBehavior标签设置并发模式:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single)]

  同样,前面提到的实力上下文模式和并发模式也是有3*3=9种组合的。

  对于单调模式(PerCall),由于每个服务调用都使用一个实例上下文,所以根本不存在并发情况,无需设置并发模式,但是对于同一个服务代理,如果需要并行发送请求,则需要手动开启服务代理,否则服务是会串行调用的(P189)。

  对于会话模式(PerSession),并发将按照ConcurrencyMode所配置的方式进行处理。

  对于单例模式(Single),不论并发请求来自一个还是多个客户端,若ConcurrencyMode是Single则串行,是Multiple则并行,对Reentrant在回调发生时也是并行的(下P195)。

  十一、限流

  为了防止请求数量过多导致服务器资源耗尽,需要在消息接收和处理系统之间建立一道闸门来限制流量,可以通过服务器端配置给服务添加行为来进行流量控制:

<behavior name="throttlingBehavior">
<serviceThrottling maxConcurrentCalls="16"
maxConcurrentInstances="116"
maxConcurrentSessions="100"/>
</behavior>

  三个属性分别为能处理的最大并发消息数量、服务实例上下文最大数量和最大并发会话数量,16、116、100分别是它们的默认值,在WCF4.0后,这些值是针对单个CPU而言的(下P204)。

WCF把书读薄(2)——消息交换、服务实例、会话与并发的更多相关文章

  1. WCF把书读薄(3)——数据契约、消息契约与错误契约

    上一篇:WCF把书读薄(2)——消息交换.服务实例.会话与并发 十二.数据契约 在实际应用当中数据不可能仅仅是以int Add(int num1, int num2)这种简单的几个int的方式进行传输 ...

  2. WCF把书读薄(4)——事务编程与可靠会话

    WCF把书读薄(3)——数据契约.消息契约与错误契约 真不愧是老A的书,例子多,而且也讲了不少原理方面的内容,不过越读越觉得压力山大……这次来稍微整理整理事务和可靠会话的内容. 十八.事务编程 WCF ...

  3. WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇]

    原文:WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇] 在[第2篇]中,我们深入剖析了单调(PerCall)模式下WCF对服务实例生命周期的控制,现在我们来 ...

  4. WCF学习笔记之消息交换模式

    在WCF通信中,有三种消息交换模式,OneWay(单向模式), Request/Reponse(请求回复模式), Duplex(双工通信模式)这三种通信方式.下面对这三种消息交换模式进行讲解. 1. ...

  5. WCF中操作的分界于调用顺序和会话的释放

    操作分界 在WCF操作契约的设计中,有时会有一些调用顺序的业务,有的操作不能最先调用,有的操作必须最后调用,比如在从一个箱子里拿出一件东西的时候,必须先要执行打开箱子的操作,而关上箱子的操作应该在一切 ...

  6. 【WCF全析(一)】--服务协定及消息模式

    上周微软开发布会说.NET支持完全跨平台和并开放Core源码的新闻,让我们顿时感到.NET要迎来它的春天.虽然早在几年前.NET就能开发Android和IOS,但是这次的跨平台把Linux都放到了微软 ...

  7. WCF初探-3:WCF消息交换模式之单向模式

    单向模式(One-Way Calls): 在这种交换模式中,存在着如下的特征: 只有客户端发起请求,服务端并不会对请求进行回复 不能包含ref或者out类型的参数 没有返回值,返回类型只能为void ...

  8. WCF初探-4:WCF消息交换模式之请求与答复模式

    请求与答复模式( Request/Reply) 这种交换模式是使用最多的一中,它有如下特征: 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型 相比Duplex来讲,这种模式强调的 ...

  9. 【架构之路之WCF全析(一)】--服务协定及消息模式

    上周微软开公布会说.NET支持全然跨平台和并开放Core源代码的新闻,让我们顿时感到.NET要迎来它的春天.尽管早在几年前.NET就能开发Android和IOS,可是这次的跨平台把Linux都放到了微 ...

随机推荐

  1. webstorm-前端javascript开发神器中文教程和技巧分享(转)

    webstorm是一款前端javascript开发编辑的神器,此文介绍webstorm的中文教程和技巧分享. webstorm8.0.3中文汉化版下载:百度网盘下载:http://pan.baidu. ...

  2. Log4net系列一:Log4net搭建之文本格式输出【转】

    前言 项目开发中,记录项目日志是必须的,如果非要说日志的重要性(日志可看做,飞机的黑匣子,或者汽车的行车记录仪),根据等级进行记录,方便我们排查相关问题,以后项目运维中,也方便很多.基本上我们进入一家 ...

  3. Oracle cursor_sharing 参数 详解

    一. 官网的说明 http://download.oracle.com/docs/cd/E11882_01/server.112/e17110/initparams042.htm#REFRN10025 ...

  4. 基于SQL调用Com组件来发送邮件

    这个需求是公司有个文控中心,如果有用增删改了文件信息希望可以发邮件通知到有权限的人.当然方式很多. 这里是用数据库作业来完成 JOB+Com,这里用的com组件是Jmail 当然你需要把com组件放到 ...

  5. 题目1539:师弟 ——最短路+DFS

    题意::从起点到终点的所有的最短路中,找出离终点有X个路口的城市一共有几个 开始我用最短路+DFS从起点开始搜,超时了 换了一种方法,从终点开始搜,AC #include<stdio.h> ...

  6. dxjk服务器安装 lamp

    安装Apache:用的下面的安装法命令用的是 yum -y install httpdhttp://blog.csdn.net/kesixin/article/details/72882469 mys ...

  7. git 统计命令

    git log --author='user' --pretty=tformat: --numstat --since='2018-01-01' --before='2018-10-10' | gaw ...

  8. $GLOBALS超级全局变量(PHP学习)

    1.$GLOBALS是一个数组,里面有所有的全局变量 2.$GLOBALS是超级全局变量,函数内部可以通过它直接操作全局变量.(严重不推荐,因为违反了封装原则) 3.通过$GLOBALS操作全局变量, ...

  9. git忽略一些提交上传的文件

    在项目开发的过程中有两种文件是不需要提交的. 1.一些很重要的配置文件 包括服务器地址 账号密码 数据库密码 公私钥等等 2.一些由于开发和沙箱环境和线上环境的差异 不能使用同一个时候 需要同一个文件 ...

  10. Linux: How to delete a disk or LUN reference from /dev

    In AIX, there is rmdev command to remove a disk/LUN from /dev directory i.e to make the disk/LUN una ...