WCF开发之实例模型(Instancing Modes)

调用场景
• 经典的客户端/服务器(C/S)应用程序
–客户端使用远程,有状态的对象并且在其生命周期内进行控制
• 分布式,可扩展的应用程序
– 通过及时释放远程对象来节约资源的使用
• 分布式单件 (比如:计数器,账单的流水号等)
–多个客户端共享状态
• 经典的无状态Web服务调用

实例模型
• 控制服务实例的生命周期
• InstanceContextMode枚举
– PerCall (每次的服务调用是没有相互关系的,每次都是一次独立的调用)
– PerSession (在同一个Session中的调用时有相互关系的,在不同Session中的调用中是没有相互影响的)
– Single (单件的模型,对所有的调用都是有相互关系的)
• ServiceBehaviorAttribute控制这个设置

PerCall模式
• 为每个调用创建新的服务对象

WCF开发之实例模型(Instancing Modes)

• PerCall服务增加了整体的吞吐量 (但是不适用于每次调用都耗时较长的操作)
– 状态不会在多次调用中存在
– 服务实例被释放
– 内存开销较小
– 不会产生并发性问题

PerCall体系结构
• 无状态调用
• 为每个请求分别实例化业务逻辑和数据层
• 不存在并发性问题

WCF开发之实例模型(Instancing Modes)

• 无状态调用可以共享缓存的内容
• 引入并发性问题以及资源占用的问题

WCF开发之实例模型(Instancing Modes)

利用Cache实际上就是用空间换取时间的一种方法,但是使用时也会带来一些问题,比如并发的处理,或者整体性能的一种平衡,比如:内存只有2G,但是cache要占用4G,这显然就不适合了,所以在开发时要追求一种系统整体的平衡性。

配置PerCall
• InstanceContextMode.PerCall
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class CounterServicePerCall:ICounterServicePerCall

Demo:

ICounterServicePerCall 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;
using  System.Windows.Forms;

namespace  WcfServiceLibrary11
{
    [ServiceContract]
    
public   interface  ICounterServicePerCall
    {
        [OperationContract]
        
int  IncrementCounter();
    }

    [ServiceBehavior(InstanceContextMode 
=  InstanceContextMode.PerCall)]
    
public   class  CounterServicePerCall : ICounterServicePerCall, IDisposable
    {
        
private   int  m_counter;
        
public   int  IncrementCounter()
        {
            m_counter
++ ;

            MessageBox.Show(
string .Format( " Counter = {0} " , m_counter));

            
return  m_counter;
        }

        
public   void  Dispose()
        {
            MessageBox.Show(
" Disposing PerCall object. " );
        }
    }
}

 

Config 代码
<? xml version = " 1.0 "  encoding = " utf-8 "   ?>
< configuration >
  
< system.web >
    
< compilation debug = " true "   />
  
</ system.web >
  
<!--  When deploying the service library project, the content of the config file must be added to the host '
  app.config file. System.Configuration does not support config files  for  libraries.  -->
  
< system.serviceModel >
    
< services >
      
< service behaviorConfiguration = " WcfServiceLibrary11.Service1Behavior "
        name
= " WcfServiceLibrary11.CounterServicePerCall " >
        
< endpoint address = ""  binding = " wsHttpBinding "  contract = " WcfServiceLibrary11.ICounterServicePerCall " >
          
< identity >
            
< dns value = " localhost "   />
          
</ identity >
        
</ endpoint >
        
< endpoint address = " mex "  binding = " mexHttpBinding "  contract = " IMetadataExchange "   />
        
< host >
          
< baseAddresses >
            
< add baseAddress = " http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary11/Service1/ "   />
          
</ baseAddresses >
        
</ host >
      
</ service >
    
</ services >
    
< behaviors >
      
< serviceBehaviors >
        
< behavior name = " WcfServiceLibrary11.Service1Behavior " >
          
<!--  To avoid disclosing metadata information, 
          
set  the value below to  false  and remove the metadata endpoint above before deployment  -->
          
< serviceMetadata httpGetEnabled = " True " />
          
<!--  To receive exception details  in  faults  for  debugging purposes, 
          
set  the value below to  true .  Set to  false  before deployment 
          to avoid disclosing exception information 
-->
          
< serviceDebug includeExceptionDetailInFaults = " False "   />
        
</ behavior >
      
</ serviceBehaviors >
    
</ behaviors >
  
</ system.serviceModel >
</ configuration >

 

Client 代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;

namespace  WindowsFormsApplication1
{
    
public   partial   class  Form1 : Form
    {
        MyServiceReference.CounterServicePerCallClient proxy;

        
public  Form1()
        {
            InitializeComponent();
            proxy 
=   new  WindowsFormsApplication1.MyServiceReference.CounterServicePerCallClient();
        }

        
private   void  button1_Click( object  sender, EventArgs e)
        {
            proxy.IncrementCounter();
        }
    }
}

每次调用的结果都是Counter=1,由此可见每次调用都创建一个新的服务对象。

会话(Session)
• WCF有四种类型的会话:
–传输会话,如:TCP或者命名管道(named pipe)
–可靠性会话
–安全会话
– 应用程序会话
应用程序会话是我们这次讨论的主题
• WCF的会话由客户端发起
– ASP.NET的会话由服务器端初始化

PerSession模式 (对于同一个Client而言,Service对象实例是共享的,不同的client的Service对象是分离的)
• 为每个客户端/代理创建新的服务对象
–缺省行为
• 吞吐量较少,内存开销增大
• 状态由服务实例维护
• 引发多线程客户端的并发问题

WCF开发之实例模型(Instancing Modes)

PerSession体系结构
• 有状态调用
• 每个会话可以缓存下游的业务逻辑和数据层
• 多线程客户端及其之间存在并发性问题
• 状态与会话紧密联系,而不是业务逻辑层

WCF开发之实例模型(Instancing Modes)

PerSession模式
• 仅当绑定支持会话时,才能够支持会话
• 可以支持会话的绑定:
– NetTcpBinding
– NetNamedPipeBinding
– WSHttpBinding
– WSFederationHttpBinding
– WSDualHttpBinding

配置PerSession
• InstanceContextMode.PerSession
–缺省设置
– 最好能够显式指定
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public classCounterServicePerSession:ICounterServicePerSession

配置会话
• 在服务契约上需要设置能够提供会话功能
• SessionMode 枚举:
– Allowed (缺省)(不推荐使用,原因是配置方式不确定性,容易导致错误)
– NotAllowed
– Required

配置会话
[ServiceContract(Namespace="http://www.thatindigogirl.com/samples/2006/06", SessionMode=SessionMode.Required)]
public interface ICounterServiceSession
{
    [OperationContract]
    int IncrementCounter();
}

Demo:

Service代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;
using  System.Windows.Forms;

namespace  WcfServiceLibrary11
{
    [ServiceContract(SessionMode
= SessionMode.Required)]
    
public   interface  ICounterServicePerSession
    {
        [OperationContract]
        
int  IncrementCounter();
    }

    [ServiceBehavior(InstanceContextMode 
=  InstanceContextMode.PerSession)]
    
public   class  CounterServicePerSession : ICounterServicePerSession, IDisposable
    {
        
private   int  m_counter;
        
public   int  IncrementCounter()
        {
            m_counter
++ ;

            MessageBox.Show(
string .Format( " Counter = {0} " , m_counter));
            

            
return  m_counter;
        }

        
public   void  Dispose()
        {
            MessageBox.Show(
" Disposing PerCall object. " );
        }
    }
}

 

Config 代码
<? xml version="1.0" encoding="utf-8"  ?>
< configuration >
  
< system.web >
    
< compilation  debug ="true"   />
  
</ system.web >
  
<!--  When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. 
-->
  
< system.serviceModel >
    
< services >
      
< service  behaviorConfiguration ="WcfServiceLibrary11.Service1Behavior"
        name
="WcfServiceLibrary11.CounterServicePerSession" >
        
< endpoint  address =""  binding ="wsHttpBinding"  contract ="WcfServiceLibrary11.ICounterServicePerSession"  bindingConfiguration ="wsHttpBindingSession" >
          
< identity >
            
< dns  value ="localhost"   />
          
</ identity >
        
</ endpoint >
        
< endpoint  address ="mex"  binding ="mexHttpBinding"  contract ="IMetadataExchange"   />
        
< host >
          
< baseAddresses >
            
< add  baseAddress ="http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary11/Service1/"   />
          
</ baseAddresses >
        
</ host >
      
</ service >
    
</ services >
    
< bindings >
      
< wsHttpBinding >
        
< binding  name ="wsHttpBindingSession"   receiveTimeout ="00:00:20" >
        
</ binding >
      
</ wsHttpBinding >
    
</ bindings >
    
< behaviors >
      
< serviceBehaviors >
        
< behavior  name ="WcfServiceLibrary11.Service1Behavior" >
          
<!--  To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment 
-->
          
< serviceMetadata  httpGetEnabled ="True" />
          
<!--  To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
< serviceDebug  includeExceptionDetailInFaults ="False"   />
        
</ behavior >
      
</ serviceBehaviors >
    
</ behaviors >
  
</ system.serviceModel >
</ configuration >

 

Client代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;

namespace  WindowsFormsApplication1
{
    
public   partial   class  Form1 : Form
    {
        MyServiceReference.CounterServicePerSessionClient proxy;

        
public  Form1()
        {
            InitializeComponent();
            proxy 
=   new  WindowsFormsApplication1.MyServiceReference.CounterServicePerSessionClient();
        }

        
private   void  button1_Click( object  sender, EventArgs e)
        {
            proxy.IncrementCounter();
        }
    }
}

这个例子可以看到,对于同一个客户端来说counter值是累加的,不同的客户端有不同的counter值。要注意的是,Session的timeout设置,如果客户端在timeout时间的范围内没有任何调用操作,那么在服务端的服务对象会自动销毁,此时如果Client再调用服务,就会发生异常。

会话ID(Session Id)
• 任何形式的会话都会生成会话信道
• 会话标示符用于将消息与正确的信道相关联
– 在会话的整个生命周期中起作用
• SessionId信道的属性
SessionServiceClient proxy=new SessionServiceClient ();
string s = proxy.InnerChannel.SessionId;

会话的生命周期
• 会话的生命周期缺省为持续10分钟
• 在每个绑定上可以通过receiveTimeout设置进行控制
<netTcpBinding>
<binding name="netTcp" receiveTimeout="00:10:00" />
</netTcpBinding>

会话的生命周期
• 可以通过操作显式地控制生命周期
• 设置OperationContractAttribute的属性
IsInitiating:ture表示当调用相关方法时WCF创建Session,false为不创建。
IsTerminating:ture表示当调用完相关方法后WCF销毁Session,false为保留。

[ServiceContract(Namespace ="http://www.thatindigogirl.com/samples/2006/06", SessionMode = SessionMode.Required)]
public interface ISessionService
{
    [OperationContract(IsInitiating = true, IsTerminating =false)]
    void StartSession();
    [OperationContract(IsInitiating = false, IsTerminating =false)]
    void IncrementCounter();
    [OperationContract(IsInitiating = false, IsTerminating =false)]
    int GetCounter();
    [OperationContract(IsInitiating = false, IsTerminating =false)]
    string GetSessionId();
    [OperationContract(IsInitiating = false, IsTerminating =true)]
    void StopSession();
}

Demo:

Service 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;
using  System.Windows.Forms;

namespace  WcfServiceLibrary11
{
    [ServiceContract(SessionMode 
=  SessionMode.Required)]
    
public   interface  ISessionService
    {
        [OperationContract(IsInitiating 
=   true , IsTerminating  =   false )]
        
void  StartSession();
        [OperationContract(IsInitiating 
=   false , IsTerminating  =   false )]
        
void  IncrementCounter();
        [OperationContract(IsInitiating 
=   false , IsTerminating  =   false )]
        
int  GetCounter();
        [OperationContract(IsInitiating 
=   false , IsTerminating  =   false )]
        
string  GetSessionId();
        [OperationContract(IsInitiating 
=   false , IsTerminating  =   true )]
        
void  StopSession();
    }

    [ServiceBehavior(InstanceContextMode 
=  InstanceContextMode.PerSession)]
    
public   class  SessionService : ISessionService
    {
        
private   int  m_counter  =   0 ;

        
public   void  StartSession()
        {
            m_counter 
=   1 ;
        }

        
public   void  IncrementCounter()
        {
            m_counter
++ ;
        }

        
public   int  GetCounter()
        {
            
return  m_counter;
        }

        
public   string  GetSessionId()
        {
            
return  OperationContext.Current.SessionId;
        }

        
public   void  StopSession()
        {
            m_counter 
=   0 ;
        }
    }
}

 

 

Config 代码
<? xml version="1.0" encoding="utf-8"  ?>
< configuration >
  
< system.web >
    
< compilation  debug ="true"   />
  
</ system.web >
  
<!--  When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. 
-->
  
< system.serviceModel >
    
< services >
      
< service  behaviorConfiguration ="WcfServiceLibrary11.Service1Behavior"
        name
="WcfServiceLibrary11.SessionService" >
        
< endpoint  address =""  binding ="wsHttpBinding"  bindingConfiguration ="wsHttpBindingSession"
          contract
="WcfServiceLibrary11.ISessionService" >
          
< identity >
            
< dns  value ="localhost"   />
          
</ identity >
        
</ endpoint >
        
< endpoint  address ="mex"  binding ="mexHttpBinding"  contract ="IMetadataExchange"   />
        
< host >
          
< baseAddresses >
            
< add  baseAddress ="http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary11/Service1/"   />
          
</ baseAddresses >
        
</ host >
      
</ service >
    
</ services >
    
< bindings >
      
< wsHttpBinding >
        
< binding  name ="wsHttpBindingSession"  receiveTimeout ="00:10:20" >
        
</ binding >
      
</ wsHttpBinding >
    
</ bindings >
    
< behaviors >
      
< serviceBehaviors >
        
< behavior  name ="WcfServiceLibrary11.Service1Behavior" >
          
<!--  To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment 
-->
          
< serviceMetadata  httpGetEnabled ="True" />
          
<!--  To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
< serviceDebug  includeExceptionDetailInFaults ="False"   />
        
</ behavior >
      
</ serviceBehaviors >
    
</ behaviors >
  
</ system.serviceModel >
</ configuration >

 

 

Client 代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;

namespace  WindowsFormsApplication1
{
    
public   partial   class  Form1 : Form
    {
        MyServiceReference.SessionServiceClient proxy;

        
public  Form1()
        {
            InitializeComponent();
        }

        
private   void  button1_Click( object  sender, EventArgs e)
        {
            proxy 
=   new  WindowsFormsApplication1.MyServiceReference.SessionServiceClient();
        }

        
private   void  button2_Click( object  sender, EventArgs e)
        {
            proxy.StartSession();
        }

        
private   void  button3_Click( object  sender, EventArgs e)
        {
            proxy.IncrementCounter();
        }

        
private   void  button4_Click( object  sender, EventArgs e)
        {
            MessageBox.Show(proxy.GetCounter().ToString());
        }

        
private   void  button5_Click( object  sender, EventArgs e)
        {
            MessageBox.Show(proxy.GetSessionId());
        }

        
private   void  button6_Click( object  sender, EventArgs e)
        {
            proxy.StopSession();
        }
    }
}

 

 

单件模式
• 为所有客户端的所有调用创建单一的服务对象
–称之为单件服务
• 通常会给吞吐量带来负面影响
• 潜在的较多内存开销
– 单一的大对象
• 状态由服务的实例维护
• 并发性问题

WCF开发之实例模型(Instancing Modes)

配置单件
• InstanceContextMode.Single
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class CounterServiceSingle:ICounterServiceSingle

单件的体系结构
• 无状态单件
• 下游业务逻辑和数据层能够被所有的请求共享
–它们通常都是无状态的
• 并发调用会引发并发性问题

WCF开发之实例模型(Instancing Modes)

• 单件也可以是有状态的
• 特别在处理无状态的业务逻辑对象时
• 状态由服务的会话ID(SessionId)跟踪
– 只有一个单件的对象

WCF开发之实例模型(Instancing Modes)

Demo:

 

Service 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;
using  System.Windows.Forms;

namespace  WcfServiceLibrary11
{
    [ServiceContract(SessionMode 
=  SessionMode.Required)]
    
public   interface  ICounterServiceSingle
    {
        [OperationContract]
        
int  IncrementCounter();
    }

    [ServiceBehavior(InstanceContextMode 
=  InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
    
public   class  CounterServiceSingle : ICounterServiceSingle
    {
        
private   int  m_counter  =   0 ;
        
public   int  IncrementCounter()
        {
            m_counter
++ ;

            MessageBox.Show(
string .Format( " Counter={0}, SessionID={1} " , m_counter, OperationContext.Current.SessionId));

            
return  m_counter; 
        }
    }
}

这样的话,每个客户端都共享了同一个服务对象,也就共享了同一个Counter。

会话总结
• PerCall适用于
–用于高可扩展性和高吞吐量的业务
• 使用PerSession服务时需要注意
–注意会话所带来的开销和潜在的超时问题
• 通常要避免使用单件模型
– 当多台客户端主机共享某个功能的时候非常有用

你可能感兴趣的:(WCF)