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

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

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

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

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

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

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

利用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对象是分离的)
• 为每个客户端/代理创建新的服务对象
–缺省行为
• 吞吐量较少,内存开销增大
• 状态由服务实例维护
• 引发多线程客户端的并发问题

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

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();
        }
    }
}

 

 

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

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

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

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

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服务时需要注意
–注意会话所带来的开销和潜在的超时问题
• 通常要避免使用单件模型
– 当多台客户端主机共享某个功能的时候非常有用

posted @ 2010-04-08 17:24  烟鬼  阅读(2212)  评论(0编辑  收藏  举报