[老老实实学WCF] 第八篇 实例化

原文:[老老实实学WCF] 第八篇 实例化

老老实实学WCF

第八篇 实例化

 

通过上一篇的学习,我们简单地了解了会话,我们知道服务端和客户端之间可以建立会话连接,也可以建立非会话连接,通信的绑定和服务协定的ServiceContract 的SessionMode属性共同决定了连接是否是会话的。会话连接在会话保持阶段服务端可以记住客户端,而非会话连接则不会,相同客户端的多次调用会被认为是不同的客户端发起的。

 

会话这个特性是许多其他特性的基础,例如我们今天要学习的实例化。连接是否是会话对实例化的过程将产生不同的影响。今天我们就来研究这个问题。

 

1. 什么是实例化

那么,什么是实例化呢?我们知道,要调用一个类的方法,如果这个类不是静态类,首先要将这个类实例化从而获得这个类的一个对象,然后在这个对象上调用方法,这是面向对象的基本知识,而我们服务端上寄存的服务,也是一个类,是一个实现了服务接口的类,因此,客户端在调用这个类的方法时,服务端一定也要将这个类先实例化出一个对象,然后在这个对象上调用方法,将结果返回给客户端。

 

我们在这里探讨的实例化就是当客户端调用服务端服务方法时,服务端如何对服务类进行实例化的问题。

2. 实例化的几种模式

我们不禁要疑问,实例化不就是new嘛,new有什么可说的,直接实例化然后调用最后返回,这似乎没什么特别的地方。

new的话题我觉得是个挺深的问题,它也一直是面向对象编程理论中比较重要的一环。如何正确的控制实例化的时间和地点是许多设计模式着手解决的问题。我们在本地编程模型中不太多关注到它,可能常用的也就是为共享资源建立一个单例,或者为上层调用建立一个工厂。甚至随用随new的情况也都稀松平常。

然而我们应该重视实例化的方式,尤其在面向服务这样的模型中,实例化方式的不同会显著的影响性能和伸缩性。

 

按照最简单的考量,每次客户端的方法调用服务端都实例化一个对象然后为其服务,调用完就把对象销毁,也就是随用随new。这样看上去最简单明了,但是性能可能不太好,创建对象和销毁对象都是有开销的。如果客户端和服务端使用会话来连接,那么服务端就可以实例化一次,然后在整个会话期间都使用这一个对象为其服务,这样开销就小一些了,而且客户端是知道会话的存在的,他和服务端对于服务对象的状态是有共识的,这样可以重复的利用服务对象,直到会话结束才销毁服务对象。如果更进一步,如果创建对象的开销太大而且对象提供的服务并没有对不同客户端的差异,可以让服务端只实例化一次,这个对象将为所有的客户端服务,无论连接是不是会话,这个对象一直也不会被销毁,除非服务停止或重新启动。

 

综上所述,服务端实例化的方式有三种,分别是"每调用实例","每会话实例"和"单一实例"。

实例化模式的指定是通过配置服务类的ServiceBehavior属性中的InstanceContextMode属性来实现的。注意,是服务类的属性,而不是服务协定。

(1) 每调用实例

将服务类的ServiceBehavior属性中的InstanceContextMode属性设置为PerCall来启用这种模式

在这种实例化模式下,客户端对服务方法的每一次调用,服务端都会new一个实例,方法调用结束即销毁,这种模式可能要频繁的花费创建对象和销毁对象的花销,因此性能比较差,但是每次调用后服务对象被销毁,对象所把持的资源也就被释放(如文件啦、数据库连接啦),因此伸缩性是最好的。

看下面的例子。

首先看加在服务类上的属性应该怎么写:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

我用的还是前几篇中用的IIS的例子,HelloWCFService.cs的源代码如下:

using System;
using System.ServiceModel;

namespace LearnWCF
{
    [ServiceContract(SessionMode = SessionMode.Required)]
    public interface IHelloWCF
    {
        [OperationContract]
        string HelloWCF();
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class HelloWCFService : IHelloWCF
    {
        private int _Counter;
        public string HelloWCF()
        {
            _Counter++;
            return "Hello, you called " + _Counter.ToString() + " time(s)";
        }
    }
}

Web.Config文件内容如下

<configuration>
  <system.serviceModel>
    <services>
      <service name="LearnWCF.HelloWCFService" behaviorConfiguration="metadataExchange">
        <endpoint address="" binding="wsHttpBinding" contract="LearnWCF.IHelloWCF"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataExchange">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

调用的客户端代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ServiceModel;

namespace ConsoleClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Services.HelloWCFClient client = new Services.HelloWCFClient();

            Console.WriteLine(client.HelloWCF());
            Console.WriteLine(client.HelloWCF());

            client.Close();

            Console.ReadLine();
        }
    }

}

F5运行结果如下:

可以看到,即使我们把服务协定的会话模式设置为Required(必须使用会话),即这个时候服务端和客户端是在进行会话连接的时候,服务端还是为两次客户端调用分别实例化了对象,所以看到计数没有增长,每次都是1。

 

(2) 每会话实例

将服务类的ServiceBehavior属性的InstanceContextMode属性设置为PerSession时启用这种模式。

如果不指定,那么这个值是默认的配置。

这种模式下服务端在受到会话的第一个调用时实例化对象,直到会话关闭才销毁对象,这样减少了对象的建立和销毁开销,性能有一定提升,但是在会话期间申请的资源如文件和数据库连接直到会话结束才会释放,伸缩性有所下降了。

看看给服务类的属性应该怎么写:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]

其他不修改,F5运行结果如下:

可以看到计数增长了,说明没有实例化新的对象,在整个会话中只有一个。

设想一下,如果此时把服务协定的SessionMode属性设为NotAllowed会怎样呢,结果会跟PerCall一样,因为PerSession模式需要会话的支持,如果协定不支持会话,服务端就不可能记得住客户端了,也可以理解成每次调用都是一个全新的会话了。

 

(3) 单一实例

把服务类的ServiceBehavior属性的InstanceContextMode属性设置为Single来启用这种模式。

在这种模式下,服务类在受到首次调用的时候实例化,然后一直不销毁,直到服务宿主关闭,在这期间无论是会话连接还是非会话连接,服务端都用这个实例进行调用。这样实际上几乎没有创建和销毁对象的开销,但是如果一旦实例把持资源,则一直都不能释放,毫无伸缩性可言。

看看服务类的ServiceBehavior属性怎么写:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

其他不改动,看看运行结果。

似乎和PerSession没有什么区别,关闭客户端程序再运行一次,结果如下:

计数器继续增长了,客户端程序已经重新运行,很显然这是个新的会话,但是服务端的实例一直没有销毁,所以计数器就继续增长了。

在这种模式下,无论我们把协定的SessionMode改成什么值,结果都是一样的,因为服务端已经认准了一个实例,无论客户端连接是不是会话了。

3. 总结

通过今天的学习,我们了解到了服务类实例化的几种模式,从PerCall到Single,性能越来越好,伸缩性越来越差,我们在实际项目中可以按照自己应用的特点来进行选择,同时要注意会话模式和实例化模式之间互相的影响:

(1) 如果协定不支持会话,那么PerSession相当于PerCall。

(2) 如果服务类为PerCall,那么协定的是否支持会话结果相同。

(3) 如果服务类为Single,那么协定是否支持会话结果相同。

 

其实还有一个并发的特性,稍复杂,我们后面再展开。

 

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-23 04:29:44

[老老实实学WCF] 第八篇 实例化的相关文章

[老老实实学WCF] 第十篇 消息通信模式(下) 双工

原文:[老老实实学WCF] 第十篇 消息通信模式(下) 双工 老老实实学WCF 第十篇 消息通信模式(下) 双工   在前一篇的学习中,我们了解了单向和请求/应答这两种消息通信模式.我们知道可以通过配置操作协定的IsOneWay属性来改变模式.在这一篇中我们来研究双工这种消息通信模式.   在一定程度上说,双工模式并不是与前面两种模式相提并论的模式,双工模式的配置方法同前两者不同,而且双工模式也是基于前面两种模式之上的.   在双工模式下,服务端和客户端都可以独立地调用对方,谁都不用等待谁的答复

[老老实实学WCF] 第五篇 再探通信--ClientBase

原文:[老老实实学WCF] 第五篇 再探通信--ClientBase 老老实实学WCF 第五篇 再探通信--ClientBase   在上一篇中,我们抛开了服务引用和元数据交换,在客户端中手动添加了元数据代码,并利用通道工厂ChannelFactory<>类创建了通道,实现了和服务端的通信.然而,与服务端通信的编程模型不只一种,今天我们来学习利用另外一个服务类ClientBase<>来完成同样的工作,了解了这个类的使用方法,我们对服务引用中的关键部分就能够理解了.   Client

[老老实实学WCF] 第三篇 在IIS中寄存服务

原文:[老老实实学WCF] 第三篇 在IIS中寄存服务 老老实实学WCF 第三篇 在IIS中寄宿服务   通过前两篇的学习,我们了解了如何搭建一个最简单的WCF通信模型,包括定义和实现服务协定.配置服务.寄宿服务.通过添加服务引用的方式配置客户端并访问服务.我们对WCF的编程生命周期有了一个最基本的了解.   在前两篇中演示的例子,一定要力求背着做下来,包括源程序.配置文件都要背着一行行的手写下来,这样才能有深刻的体会.WCF的知识零散复杂,必须扎扎实实的学习和练习.如果你还没有做到了然于胸,现

[老老实实学WCF] 第六篇 元数据交换

原文:[老老实实学WCF] 第六篇 元数据交换 老老实实学WCF 第六篇 元数据交换   通过前两篇的学习,我们了解了WCF通信的一些基本原理,我们知道,WCF服务端和客户端通过共享元数据(包括服务协定.服务器终结点信息)在两个终结点上建立通道从而进行通信.我们通过手写代码(或配置)的方式为服务端编写了元数据信息,没有借助元数据交换就实现了通信.然而在实际应用中,元数据往往是很多的,而且重复编写元数据的工作也是不值得的,因此必然会用到元数据交换的方式让客户端获取元数据,本篇我们就来进一步了解一下

[老老实实学WCF] 第九篇 消息通信模式(上) 请求应答与单向

原文:[老老实实学WCF] 第九篇 消息通信模式(上) 请求应答与单向 老老实实学WCF 第九篇 消息通信模式(上) 请求应答与单向   通过前两篇的学习,我们了解了服务模型的一些特性如会话和实例化,今天我们来进一步学习服务模型的另一个重要特性:消息通信模式.   WCF的服务端与客户端在通信时有三种模式:单向模式.请求/应答模式和双工模式.   如果选用了单向模式,调用方在向被调用方进行了调用后不期待任何回应,被调用方在执行完调用后不给调用方任何反馈.如客户端通过单向模式调用了一个服务端的操作

[老老实实学WCF] 第一篇 Hello WCF

原文:[老老实实学WCF] 第一篇 Hello WCF 老老实实学WCF  第一篇 Hello WCF   WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用程序的编程模型,涉及面之广,技术之复杂,结构之零散,让我们初学这门技术的菜鸟时常有无处下手的感觉,此系列博文系笔者艰难探索WCF技术过程的学习笔记,笔者抱着老老实实的态度,力图扎扎实实,循序渐进地学好这门技术,文中难免有疏漏和错误之处,还请

【强烈强烈推荐】《ORACLE PL/SQL编程详解》全原创(共八篇)--系列文章导航

原文:[强烈强烈推荐]<ORACLE PL/SQL编程详解>全原创(共八篇)--系列文章导航 <ORACLE PL/SQL编程详解>    系列文章目录导航     --通过知识共享树立个人品牌.           本是成书的,但后来做其他事了,就无偿的贡献出来,被读者夸其目前为止最"实在.经典"的写ORACLE PL/SQL编程的文章-!   觉得对你有帮助,请留言与猛点推荐,谢谢.     [推荐]ORACLE PL/SQL编程详解之一:PL/SQL 程序

一步一步学ROP之linux_x64篇

一步一步学ROP之linux_x64篇 一.序 **ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等).上次我们主要讨论了linux_x86的ROP攻击:<一步一步学ROP之linux_x86篇>,在这次的教程中我们会带来上一篇的补充以及linux_x64方面的ROP利用方法,欢迎大家继续学习. 另外文中涉及代码可在我的github下载:https://githu

ASP.NET分页组件学与用——教学篇

asp.net|分页 ASP.NET分页组件学与用--教学篇 没有人会怀疑分页组件在WEB应用程序中的作用.数据库中的记录数成千上万甚至过亿,如果一股脑儿显示在一页显然毫不现实,这样的程序员也太小儿科了.所以,最好的办法就是分页显示,每页只显示数据库中的一部分记录,可以翻页,也可以输入一个页码翻到指定的页面,这种方式也是当前比较常见的用法. 本文的不同之处在于,我把分页的功能封装在组件中,一方面体现了面向对象的特点,另一方面也方便发布.共享和使用.事先声明,本文不再讲述组件创建的详细过程,如果有