Artech

Develop every application as an art using the most suitable technologies!

常用链接

统计

积分与排名

网上邻居

我的博文系列

最新评论

[原创]我的WCF之旅(8):WCF中的Session和Instancing Management

WCF中的Session

我们知道,WCF是MS基于SOA建立的一套在分布式环境中各个相对独立的Application进行Communication的构架。他实现了最新的基于WS-*规范。按照SOA的原则,相对独自的业务逻辑以service的形式封装,调用者通过Messaging的方式调用Service。对于承载着某个业务功能的实现的Service应该具有Context无关性、甚至是Solution无关性,也就是说个构成Service的operation不应该绑定到具体的调用上下文,对于任何调用,具有什么样的输入,就会有与之对应的输出。因为SOA的一个最大的目标就是尽可能地实现重用,只有具有Context无关性/Solution无关性,Service才能实现最大限度的重用。此外Service的Context无关性/Solution无关性还促进了另一个重要的面向服务的特征的实现:可组合性,把若干相关细粒度的Service封装成一个整体业务流程的Service。

在一个C/S(Client/Service)场景中,Context无关性体现在Client对Service的每次调用都是完全不相关的。但是在有些情况下,我们却希望系统为我们创建一个Session来保留某个Client和Service的进行交互的状态。所以,像Web Service一样,WCF也提供了对Session的支持。对于WCF来说,Client和Service之间的交互都通过Soap Message来实现的,每次交互的过程就是一次简单的Message Exchange。所以从Messaging的角度来讲,WCF的Session就是把某个把相关的Message Exchange纳入同一个Conversation。每个Session用一个Session ID来唯一标识。

WCF中的Session和ASP.NET的Session

在WCF中,Session属于Service Contract的范畴,是一个相对抽象的概念,并在Service Contract定义中通过SessionModel参数来实现。他具有以下几个重要特征:

  • Session的创建和结束都有来自Client端的调用来实现

我们知道,在WCF中Client通过创建的Proxy对象来和service的交互,在默认的支持Session的情况下,Session和具体的Proxy对象绑定在一起,当Client通过调用Proxy的某个方法来访问Service的时候,Session被初始化,直到Proxy被关闭,Session被终止,我们可以通过下面两种方式来关闭Proxy:

  1. 调用System.ServiceModel. ICommunicationObject对象(我们一般通过System.ServiceModel. ChannelFactory对象的CreateChannel方法获得)的Close方法。
  2. 调用System.ServiceModel. ClientBase对象(我们一半通过继承它来实现我们为某个特定的Service创建Proxy类)的Close方法。

此外,我们也可以人为地指定通过调用Service的某个operation来初始化、或者终止Session。我们一般通过System.ServiceModel. OperationContractAttribute的IsInitiating和IsTerminating参数来指定初始化和终止Session的Operation。

  • WCF保证处于某个Session中传递的Message按照他发送的次序被接收
  • WCF并没有为Session的支持而保存相关的状态数据。

说道WCF中的Session,我们很自然地联想到ASP.NET中的Session。实际上,他们之间具有很大的差异:

  • ASP.NET的Session总是在Server端初始化的。
  • ASP.NET并不提供Ordered Message Delivery的担保。
  • ASP.NET是通过在Serer以某种方式保存State来实现对Session的支持的,比如保存在Web Server的内存中,保存在State Server甚至是SQL Server中。

WCF中的Session的实现和Instancing Management

在上面我们说了,虽然WCF支持Session,但是并没有相关的状态信息被保存在某种介质中。WCF是通过怎样的方式来支持Session的呢?这就是我们本节要讲的Instancing Management。

对于Client来说,它实际上不能和Service进行直接交互,它只能通过客户端创建的Proxy来间接地实现和service的交互。Session的表现体现在以下两种方式:

  • Session的周期和Proxy的周期绑定,这种方式体现为默认的Session支持。
  • Session的周期绑定到开始和终止Session的方法调用之间的时间内,这种方式体现在我们在定义Operation Contract时通过IsInitiating和IsTerminating显式指定开始和终止Session的Operatoin。

我们很清楚,真正的逻辑实现是通过调用真正的Service instance中。在一个分布式环境中,我们把通过Client的调用来创建最终的Service Instance的过程叫做Activation。在Remoting中我们有两种Activation方式:Server Activation(Singleton和SingleCall),Client Activation。实际上对WCF也具有相似的Activation。不过WCF不仅仅创建对应的Service Instance,而且还构建相关的Context, 我们把这些统称为Instance Context。不同的Activation方式在WCF中体现为的Instance context model。不同的Instance Context Mode体现为Proxy、Service 调用和Service Instance之间的对应关系。可以这么说,Instance Context Mode决定着不同的Session表现。在WCF中,支持以下3中不同级别的Instance Context Mode:

  • PerCall:WCF为每个Serivce调用创建 一个Service Instance,调用完成后回收该Instance。这种方式和Remoting中的SingleCall相似。
  • PerSession:在Session期间的所有Service调用绑定到某一个Service Instance,Session被终止后,Service Instance被回收。所以在Session结束后使用同一个Proxy进行调用,会抛出Exception。这种方式和Remoting中的CAO相似。
  • Singleton:这种方式和Remoting的Singelton相似。不过它的激活方式又有点特别。当为对应的Service type进行Host的时候,与之对应的Service Instance就被创建出来,此后所有的Service调用都被forward到该Instance。

WCF的默认的Instance Context Mode为PerSession,但是对于是否对Session的支持,Instancing的机制有所不同。如果通过以下的方式定义ServiceContract使之不支持Session,或者使用不支持Session的Binding(顺便说一下,Session的支持是通过建立Sessionful Channel来实现的,但是并不是所有的Binding都支持Session,比如BasicHttpBinding就不支持Session),WCF实际上会为每个Service调用创建一个Service Instance,这实质上就是PerCall的Instance Context Mode,但我为什么会说默认的是PerSession呢?我个人觉得我们可以这样地来看看Session:Session按照本意就是Client和Service之间建立的一个持续的会话状态,不过这个Session状态的持续时间有长有短,可以和Client的生命周期一样,也可以存在于某两个特定的Operation调用之间,最短的则可以看成是每次Service的调用,所以按照我的观点,PerCall也可以看成是一种特殊的Session(我知道会有很多人不认同我的这种看法。)

   [ServiceContract(SessionMode = SessionMode.NotAllowed)]

Simple

接下来我们来看看一个简单的Sample,相信大家会对Session和Instancing Management会有一个深入的认识。这个Sample沿用我们Calculator的例子,Solution的结构如下,4个Project分别用于定义SeviceContract、Service Implementation、Hosting和Client。


我们先采用默认的Session和Instance Context Modle,在这之前我们看看整个Solution各个部分的定义:

1.    Service Contract:ICalculator

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace Artech.SessionfulCalculator.Contract
{
    [ServiceContract]
    
public interface ICalculator
    
{
        [OperationContract(IsOneWay 
= true)]
        
void  Adds(double x);

        [OperationContract]
        
double GetResult();
    }

}

2.    Service Implementation:CalculatorService

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Artech.SessionfulCalculator.Contract;

namespace Artech.SessionfulCalculator.Service
{
    
public class CalculatorService:ICalculator
    
{

        
private double _result;

        
ICalculator Members

        
public CalculatorService()
        
{
            Console.WriteLine(
"Calculator object has been created");
        }


        
~CalculatorService()
        
{
            Console.WriteLine(
"Calculator object has been destoried");
        }


    }

}

为了让大家对Service Instance的创建和回收有一个很直观的认识,我特意在Contructor和Finalizer中作了一些指示性的输出。同时在每个Operation中输出的当前的Session ID

3.    Hosting

Program

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Artech.SessionfulCalculator.Service;
using System.Threading;

namespace Artech.SessionfulCalculator.Hosting
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            
using(ServiceHost host = new ServiceHost(typeof(CalculatorService)))
            
{
                host.Opened 
+= delegate
                
{
                    Console.WriteLine(
"The Calculator service has begun to listen");
                }
;
                host.Open();
                Timer timer 
= new Timer(delegate { GC.Collect(); }null0100);
                Console.Read();
            }

        }

    }

}

除了Host CalculatorService之外,我还通过一个Timer对象每隔一个很短的时间(0.1s)作一次强制的垃圾回收,使我们通过输出看出Service Instance是否被回收了。

Configuration

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<system.serviceModel>   
    
<behaviors>
      
<serviceBehaviors>
        
<behavior name="CalculatorBehavior">
          
<serviceMetadata httpGetEnabled="true" />
        
</behavior>
      
</serviceBehaviors>
    
</behaviors>
    
<services>
      
<service behaviorConfiguration="CalculatorBehavior" name="Artech.SessionfulCalculator.Service.CalculatorService">
        
<endpoint address="" binding="basicHttpBinding" bindingConfiguration=""
          contract
="Artech.SessionfulCalculator.Contract.ICalculator" />
        
<host>
          
<baseAddresses>
            
<add baseAddress="http://localhost:9999/SessionfulCalculator" />
          
</baseAddresses>
        
</host>
      
</service>
    
</services>
  
</system.serviceModel>
</configuration>

我们使用的是basicHttpBinding

4.    Client

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Artech.SessionfulCalculator.Contract;

namespace Artech.SessionfulCalculator.Client
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            ChannelFactory
<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("httpEndpoint");
            Console.WriteLine(
"Create a calculator proxy: proxy1");
            ICalculator proxy1 
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy1.Adds(1)");
            proxy1.Adds(
1);
            Console.WriteLine(
"Invocate  proxy1.Adds(2)");
            proxy1.Adds(
2);
            Console.WriteLine(
"The result return via proxy1.GetResult() is : {0}", proxy1.GetResult());

            Console.WriteLine(
"Create a calculator proxy: proxy2");
            ICalculator proxy2
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy2.Adds(1)");
            proxy2.Adds(
1);
            Console.WriteLine(
"Invocate  proxy2.Adds(2)");
            proxy2.Adds(
2);
            Console.WriteLine(
"The result return via proxy2.GetResult() is : {0}", proxy2.GetResult());

            Console.Read();
        }

    }

}

我创建了两个Proxy:Proxy1和Proxy2,并以同样的方式调用它们的方法:Add->Add->GetResult。

Configuration

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
<system.serviceModel>
        
<client>
            
<endpoint address="http://localhost:9999/SessionfulCalculator"
                binding
="basicHttpBinding" contract="Artech.SessionfulCalculator.Contract.ICalculator"
                name
="httpEndpoint" />
        
</client>
    
</system.serviceModel>
</configuration>

我们来看看运行的结果:

Client端:


虽然我们我们两次调用Add方法进行累加,但是最终的结果 依然是0。这好像和我们开始所说的WCF默认的Session支持不相符,默认的Session支持是这样:Service Instance和Proxy绑定在一起,当调用Proxy的任何一个方法的时候Session开始,从此Session将会和Proxy具有一样的生命周期。但是这样的一个前提的,我们需要通过支持Session的Binding来创建我们的Sessionful Channel。显然basicHttpBinding是不支持Session的,所以WCF会采用PerCall的方式创建Service Instance。同时由于不支持Session的Binding,Session ID为null。所以我们会很容易想到,我们进行的每次Service的调用都会在Service端创建一个不同Instance,Host的输出证明了这一点。


既然我们说上面的执行结构是由于不支持Session的basicHttpBinding造成的,那么我们现在来使用一个支持Session的Binding:wsHttpBinding。我们只需改变Hosting的Endpoint的配置:

<endpoint address="" binding="wsHttpBinding" bindingConfiguration=""
          contract
="Artech.SessionfulCalculator.Contract.ICalculator" />

和Client的Endpoint的配置:

<endpoint address="http://localhost:9999/SessionfulCalculator"
                binding
="wsHttpBinding" contract="Artech.SessionfulCalculator.Contract.ICalculator"
                name
="httpEndpoint" />

现在再来看看执行的结果,首先看看Client:


从两个Proxy的最后 结果返回3,可以看出我们默认的Session起作用了。而且我们会容易想到,此时Server端会有两个Service Instance被创建。进一步地,由于Client的Proxy还依然存在,Service Instance也不会被回收掉,我们通过Host的输出来验证这一点:


从输出可以看出,Constructor来两次调用,这说明了两个Service Instance被创建,基于同一个Service Instance的调用具有相同的Session ID。没有Finalizer相应的输出,说明Service Instance依然存在。除非你在Client端Close掉Proxy。

我现在就来通过修改Client端的来Close掉Proxy:通过ICommunicationObject.Close来显式地close掉Proxy

static void Main(string[] args)
        
{
            ChannelFactory
<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("httpEndpoint");
            Console.WriteLine(
"Create a calculator proxy: proxy1");
            ICalculator proxy1 
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy1.Adds(1)");
            proxy1.Adds(
1);
            Console.WriteLine(
"Invocate  proxy1.Adds(2)");
            proxy1.Adds(
2);
            Console.WriteLine(
"The result return via proxy1.GetResult() is : {0}", proxy1.GetResult());
            (proxy1 
as ICommunicationObject).Close();

            Console.WriteLine(
"Create a calculator proxy: proxy2");
            ICalculator proxy2
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy2.Adds(1)");
            proxy2.Adds(
1);
            Console.WriteLine(
"Invocate  proxy2.Adds(2)");
            proxy2.Adds(
2);
            Console.WriteLine(
"The result return via proxy2.GetResult() is : {0}", proxy2.GetResult());
            (proxy1 
as ICommunicationObject).Close();

            Console.Read();
        }

那么我们现在看运行后Host的输出,就会发现Finalizer被调用了:


上面演示了默认的Session和Instancing Management,我们现在来显式地制定Session Model,我们先修改ServiceContract使之不支持Session:

[ServiceContract(SessionMode = SessionMode.NotAllowed)]
    
public interface ICalculator
    
{
        [OperationContract(IsOneWay 
= true)]
        
void  Adds(double x);

        [OperationContract]
        
double GetResult();
    }

看看Client的输出:


从最后的结果为0可以知道Session确实没有起作用。我们说用Client基于Session的表现,其根本是Server端的Instancing。从上面可以看出,Server实际上是采用PerCall的Instance Context Model。我们可以从Hosting的输出得到验证:


上面对不支持Session作了实验,我们现在来显式地允许Session,并制定开始和终止Session的Operation:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace Artech.SessionfulCalculator.Contract
{
    [ServiceContract(SessionMode 
= SessionMode.Required)]
    
public interface ICalculator
    
{
        [OperationContract(IsOneWay 
= true, IsInitiating = true, IsTerminating = false)]
        
void  Adds(double x);

        [OperationContract(IsInitiating 
= false,IsTerminating =true)]
        
double GetResult();
    }

}

为了模拟当Session终止后继续调用Proxy的场景,我进一步修改了Client的代码:

class Program
    
{
        
static void Main(string[] args)
        
{
            ChannelFactory
<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("httpEndpoint");
            Console.WriteLine(
"Create a calculator proxy: proxy1");
            ICalculator proxy1 
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy1.Adds(1)");
            proxy1.Adds(
1);
            Console.WriteLine(
"Invocate  proxy1.Adds(2)");
            proxy1.Adds(
2);
            Console.WriteLine(
"The result return via proxy1.GetResult() is : {0}", proxy1.GetResult());
            Console.WriteLine(
"Invocate  proxy1.Adds(1)");
            
try
            
{
                proxy1.Adds(
1);
            }

            
catch (Exception ex)
            
{
                Console.WriteLine(
"It is fail to invocate the Add after terminating session because \"{0}\"", ex.Message);
            }



            Console.WriteLine(
"Create a calculator proxy: proxy2");
            ICalculator proxy2
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy2.Adds(1)");
            proxy2.Adds(
1);
            Console.WriteLine(
"Invocate  proxy2.Adds(2)");
            proxy2.Adds(
2);
            Console.WriteLine(
"The result return via proxy2.GetResult() is : {0}", proxy2.GetResult());
            

            Console.Read();
        }

现在看看 Client的输出结果:


我们发现当我们调用GetResult之后再次调用Add方法,Exception被抛出。原因很简单,因为我们把GetResult方法标识为终止Session的Operation。所以当该方法被调用之后,Session被终止,对应的Service Instance也标识为可回收对象,此时再次调用,显然不能保证有一个对应的Service Instance来Handle这个调用,显然这是不允许的。

以上我们对采用默认的Instance Context Model,不同的Session Model。现在我们反过来,在Session支持的前提下,采用不同Instance Context Model,看看结果又如何:

我们把Client端的代码回到最初的状态:

static void Main(string[] args)
        
{
            ChannelFactory
<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("httpEndpoint");
            Console.WriteLine(
"Create a calculator proxy: proxy1");
            ICalculator proxy1 
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy1.Adds(1)");
            proxy1.Adds(
1);
            Console.WriteLine(
"Invocate  proxy1.Adds(2)");
            proxy1.Adds(
2);
            Console.WriteLine(
"The result return via proxy1.GetResult() is : {0}", proxy1.GetResult());

            Console.WriteLine(
"Create a calculator proxy: proxy2");
            ICalculator proxy2
= calculatorChannelFactory.CreateChannel();
            Console.WriteLine(
"Invocate  proxy2.Adds(1)");
            proxy2.Adds(
1);
            Console.WriteLine(
"Invocate  proxy2.Adds(2)");
            proxy2.Adds(
2);
            Console.WriteLine(
"The result return via proxy2.GetResult() is : {0}", proxy2.GetResult());

            Console.Read();
        }

通过在Calculator Service上面运用ServiceBehavior,并指定InstanceContextMode为PerCall:

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

虽然我们ServiceContract被显式指定为支持Session,看看运行的结果是否如此:


看来并非如此,所以我们说client端表现出的Session实际上是对应的Instancing来实现的,现在采用PerCall的Instance Context Mode, Proxy的状态是不可能被保留的。如果现在我们把Instance Context Mode设为PerSession,运行结果将会如我们所愿,现在我就不再演示了。

我们来看看Single的Instance Context Mode:

ServiceBehavior(InstanceContextMode =  InstanceContextMode.Single)]
public class CalculatorService:ICalculator
{
  
}

我们这次先来看Hosting的输出结果,这是在刚刚启动Hosting,Client尚未启动时的Screenshot。


在这之前我们都是Client通过Proxy调用相应的Service之后,Service Instance才开始创建,但是对于InstanceContextMode.Single,Service Instance却早在Service Type被Host的时候就已经被创建了。

现在启动Client:


同原来不一样的是,第二个Proxy返回的结果是6而不是3,这是因为只有一个Service Instance,所有调用的状态都将保留。从Hosting的输出也可以验证这一点:


WCF相关内容:
[原创]我的WCF之旅(1):创建一个简单的WCF程序
[原创]我的WCF之旅(2):Endpoint Overview
[原创]我的WCF之旅(3):在WCF中实现双向通信(Bi-directional Communication)
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part I
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part II
[原创]我的WCF之旅(5):Service Contract中的重载(Overloading)
[原创]我的WCF之旅(6):在Winform Application中调用Duplex Service出现TimeoutException的原因和解决方案
[原创]我的WCF之旅(7):面向服务架构(SOA)和面向对象编程(OOP)的结合——如何实现Service Contract的继承
[原创]我的WCF之旅(8):WCF中的Session和Instancing Management
[原创]我的WCF之旅(9):如何在WCF中使用tcpTrace来进行Soap Trace
[原创]我的WCF之旅(10): 如何在WCF进行Exception Handling
[原创]我的WCF之旅(11):再谈WCF的双向通讯-基于Http的双向通讯 V.S. 基于TCP的双向通讯

[原创]我的WCF之旅(12):使用MSMQ进行Reliable Messaging
[原创]我的WCF之旅(13):创建基于MSMQ的Responsive Service

posted on 2007-06-13 01:59 Artech 阅读(6854) 评论(55)  编辑 收藏 网摘

评论

#1楼 2007-06-13 02:25 Tim[未注册用户]

从头到尾看下来,对WCF的Session有了深入的认识,这是我所见的写的相关的最深入的文章。
谢谢!
  回复  引用    

#2楼 2007-06-13 02:26 哈哈娃[未注册用户]

看来LZ对WCF确实有很深的研究!   回复  引用    

#3楼[楼主] 2007-06-13 07:30 Artech      

@Tim
@哈哈娃
谢谢两位的关注
  回复  引用  查看    

#4楼 2007-06-13 08:38 David Fan      

你的这一系列文章真的很不错,谢谢   回复  引用  查看    

#5楼 2007-06-13 08:53 Justin      

顶一下!LZ的文章值得学习!   回复  引用  查看    

#6楼[楼主] 2007-06-13 10:44 Artech      

@David Fan
@Justin
这一系列,已经停了很久了,今天开始继续。
  回复  引用  查看    

#7楼 2007-06-13 13:22 随风飘散      

不错加油,好文已经收藏。   回复  引用  查看    

#8楼[楼主] 2007-06-13 13:24 Artech      

@随风飘散
:)
  回复  引用  查看    

#9楼 2007-06-13 20:21 编写人生

顶,绝对顶。   回复  引用    

#10楼[楼主] 2007-06-13 20:34 Artech      

@编写人生
呵呵,谢谢你的“顶”!
  回复  引用  查看    

#11楼 2007-08-06 18:13 sean[未注册用户]

非常不错,谢谢   回复  引用    

#12楼 2008-01-24 16:48 steven huang [未注册用户]

看到這裡,對WCF 有了認識,3Q 樓主   回复  引用    

#13楼 2008-02-16 14:29 alec318[未注册用户]

真的很棒,很有用,帮我解决了很多问题   回复  引用    

#14楼 2008-03-04 15:18 tloner      

Artech出品,必属精品!收藏,谢谢!   回复  引用  查看    

#15楼 2008-03-06 08:39 tloner      

请教Artech。
怎么实现 Instance pool ?
池里面的Instance不通过Client回收,而是通过Host来管理。每一次Client Call都在Pool里寻找可用的Instance,如果没有可用的Instance则等待,有点像企业级应用。
好像上面的三种Session方式不能完美解决这个问题,Artech有研究过这种情况吗?谢谢
  回复  引用  查看    

#16楼[楼主] 2008-03-06 12:28 Artech      

@tloner
没有进行过深入的研究, 不过我想可以通过WCF Extension, 自定义InstanceProvider按照你的意愿创建Service Instance
  回复  引用  查看    

#17楼 2008-03-08 10:40 tloner      

有些Instance(比如GIS服务)的创建很耗时间,PerCall和PerSession的方式不适合,而Singleton又不能处理海量Client Call,所以说 Instance pool 是有必要的。这种需求应该比较常见。
Com+有这种特性的,我想WCF应该也考虑到了这方面的需求。上面Artech说可以通过WCF Extension实现Pool,有什么资料可以研究一下吗?谢谢
  回复  引用  查看    

#18楼[楼主] 2008-03-10 14:13 Artech      

@tloner
个人觉得MSDN上面的内容是最全的了http://msdn2.microsoft.com/en-us/library/ms733848.aspx" target="_new">http://msdn2.microsoft.com/en-us/library/ms733848.aspx
  回复  引用  查看    

#19楼 2008-03-13 17:25 tloner      

非常感谢!   回复  引用  查看    

#20楼 2008-03-22 16:18 richardguo[未注册用户]

tloner提到Singleton不能处理海量Client Call,请问这是为什么呢?   回复  引用    

#21楼 2008-03-25 12:39 tloner      

@ richardguo

Singleton保证只有一个Instance,在处理一个Client Call的时候,其它Client Call全都排除等候,如果海量Client Call时,很有可能触发TimeoutExcpetion
  回复  引用  查看    

#22楼 2008-03-27 19:49 tloner      

更正一下上面的说法:
决定Client Call是否排队应该取决于ConcurrencyMode,而不是InstanceContextMode。

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Multiple)]
public class Service1 : IService1
{
}
要注意线程安全的问题。
  回复  引用  查看    

#23楼 2008-04-17 09:32 tloner      

@Artech
请教一个问题:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Single)]
public class Service1 : IService1
{}
并发访问的时候,客户端请求没有排队。
当改成下面:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Single)]
public class Service1 : IService1
{}
请求才会排队。
是不是percall实例模式不支持ConcurrencyMode
  回复  引用  查看    

#24楼[楼主] 2008-04-18 09:14 Artech      

@tloner
我想是因为PerCall不会造成对Session Instance的争用。
  回复  引用  查看    

#25楼 2008-04-19 16:44 BAsil      

写的很好,支持一下   回复  引用  查看    

#26楼 2008-05-27 13:12 平静中的疯狂      

看一篇,叫声好http://www.cnblogs.com/Emoticons/tusiji/203330988.gif" alt="" />   回复  引用  查看    

#27楼 2008-08-29 11:48 王德水      

这篇文章写的真好,我看Programming wcf services都没搞的很清楚,看了你的这篇,太清晰了,啥都不说了,刚刚的。。。   回复  引用  查看    

#28楼 2008-10-08 11:35 gbx[未注册用户]

首先感谢楼主的辛苦而高品质的工作。

我看了楼主的文章,试用了上面的代码,确实很有帮助。

可是我现在需要做一个MIS, 用户使用用户名/密码通过SSL登录到服务器;然后服务器查询数据库验证身份,如果合法为其分配一个session id;然后客户端/服务器的连接断开。

一定时间后,客户端向服务器发送一个请求(比如自己的帐户余额),客户端在请求包中附加已分配的session id。 服务器检查这个session id,如果合法,就找到这个用户的帐户,查询数据库返回其账户余额。(当然,可以视请求的重要性来决定是不是通过ssl传递数据)。只有等用户退出或者超时之后,分配的session id才被删除。

问题是:看了楼主的文章,除了single方式以外,per-call/per-session当连接断开以后,session id就会改变,这样发另外的请求就需要重新验证身份,很繁琐。

我的解决方案是自己管理session id, 可是又觉得好像没必要,WCF应该提供这样的机制。

我和做java的朋友讨论过,他说jservlet有这样的机制。

想听听楼主的见解,thanks
  回复  引用    

#29楼 2008-12-05 14:13 zhichiyan      

楼主讲的太清楚了,我的问题已经不存在了。
楼主真的是花了很多心血,喝彩一下!
  回复  引用  查看    

#30楼 2008-12-18 12:14 zhichiyan      

当binding="wsHttpBinding"时,按理说,默认情况下,服务实例模型应该为persession 。
但是,如果在app.config 中增加如下配置:
<bindings>
<wsHttpBinding>
<binding name="NewBinding0" maxReceivedMessageSize="65536">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
bindingConfiguration="NewBinding0"
时,服务实例模型似乎变成了perCall,不知道何故。
但是,当binding="nettcpBinding"时,无此现象。
  回复  引用  查看    

#31楼 2008-12-19 19:20 zhichiyan      

楼主出书吧,我一定支持你   回复  引用  查看    

#32楼[楼主] 2008-12-26 09:19 Artech      

--引用--------------------------------------------------
zhichiyan: 当binding=&quot;wsHttpBinding&quot;时,按理说,默认情况下,服务实例模型应该为persession 。
但是,如果在app.config 中增加如下配置:
&lt;bindings&gt;
&lt;wsHttpBinding&gt;
&lt;binding name=&quot;NewBinding0&quot; maxReceivedMessageSize=&quot;65536&quot;&gt;
&lt;security mode=&quot;None&quot; /&gt;
&lt;/binding&gt;
&lt;/wsHttpBinding&gt;
&lt;/bindings&gt;
bindingConfiguration=&quot;NewBinding0&quot;
时,服务实例模型似乎变成了perCall,不知道何故。
但是,当binding=&quot;nettcpBinding&quot;时,无此现象。
--------------------------------------------------------
wsHttpBinding在Security Enabled或者Reliable Session Enabled的时候再支持Session!
  回复  引用  查看    

#33楼[楼主] 2008-12-26 09:19 Artech      


--引用--------------------------------------------------
zhichiyan: 楼主出书吧,我一定支持你
--------------------------------------------------------
可惜像你这样的买家太少:)
  回复  引用  查看    

#34楼 2008-12-29 15:10 学习wcf中[未注册用户]

ChannelFactory<ICalculator> calculatorChannelFactory = new ChannelFactory<ICalculator>("httpEndPoint");
Console.WriteLine("Create a calculator proxy: porxy1");
ICalculator proxy1 = calculatorChannelFactory.CreateChannel();
Console.WriteLine("Invocate proxy1.Add(1)");
proxy1.Add(1);
Console.WriteLine("Invocate proxy1.Add(2)");
proxy1.Add(2);
Console.WriteLine("The result return via proxy1.GetResult() is: {0}", proxy1.GetResult());
(proxy1 as ICommunicationObject).Close();

Console.WriteLine("Create a calculator proxy: proxy2");
ICalculator proxy2 = calculatorChannelFactory.CreateChannel();
Console.WriteLine("Invocate proxy2.Add(1)");
proxy2.Add(1);
Console.WriteLine("Invocate proxy2.Add(2)");
proxy2.Add(2);
Console.WriteLine("The result return via proxy2.GetResult() is: {0}", proxy2.GetResult());
(proxy2 as ICommunicationObject).Close();

中的最后一句 (proxy2 as ICommunicationObject).Close();
不会出现Console.WriteLine("Calculator object has been destoried");
即没有调用析构函数,没有摧毁对象
我把timer给注释掉了
请问为什么
  回复  引用    

#35楼[楼主] 2008-12-30 08:46 Artech      

@学习wcf中
GC执行的时机是CLR控制,我通过Timer显示调用GC就是强制GC执行!
  回复  引用  查看    

#36楼 2009-02-12 10:52 Cary Xiao[未注册用户]

Artech, “例子”应该是Sample,楼主写成了Simple. :-)   回复  引用    

#37楼[楼主] 2009-02-16 08:05 Artech      

@Cary Xiao
笔误:)
  回复  引用  查看    

#38楼 2009-03-05 11:19 真水无香1981[未注册用户]

代码哪里下载   回复  引用    

#39楼 2009-03-11 10:57 SAL      

看了博主的文章,不顶都不行啊。如果博主你出书,不管什么书我都会支持你的。   回复  引用  查看    

#40楼 2009-03-15 02:19 sps.shareach.com      

InstanceContextMode=InstanceContextMode.Single
是常说的单例多例模式
ConcurrencyMode=ConcurrencyMode.Multiple
多线程,确定客户端请求有没有排队
这两个之间没有必然的联系吧?DCom中也有这种

不过感觉如果设成
InstanceContextMode=InstanceContextMode.PerCall或者PerSession
ConcurrencyMode=ConcurrencyMode.Multiple
的时候有点怪,呵呵
  回复  引用  查看    

#41楼 2009-03-15 02:24 sps.shareach.com      

Artech
在PerSession时候, 服务端好像在客户端退出后不会自动销毁Session, 不知道怎么回事?
总体看懂了WCF Session,好文啊, 看了2天,终于到这了,不知道还要看多少天
  回复  引用  查看    

#42楼 2009-03-15 02:41 sps.shareach.com      

不知道客户端实例化代理类后长时间不操作, 对session影响是什么?   回复  引用  查看    

#43楼[楼主] 2009-03-16 08:38 Artech      

--引用--------------------------------------------------
SAL: 看了博主的文章,不顶都不行啊。如果博主你出书,不管什么书我都会支持你的。
--------------------------------------------------------
倒时候卖不出去,我就都卖给你:)
  回复  引用  查看    

#44楼[楼主] 2009-03-16 08:40 Artech      

--引用--------------------------------------------------
sps.shareach.com: InstanceContextMode=InstanceContextMode.Single
是常说的单例多例模式
ConcurrencyMode=ConcurrencyMode.Multiple
多线程,确定客户端请求有没有排队
这两个之间没有必然的联系吧?DCom中也有这种

不过感觉如果设成
InstanceContextMode=InstanceContextMode.PerCall或者PerSession
ConcurrencyMode=ConcurrencyMode.Multiple
的时候有点怪,呵呵
--------------------------------------------------------
这两个是不同的概念,InstanceContextMode涉及到的是服务实力的激活方式和生命周期管理。ConcurrencyMode表示是否允许多线程对服务实例的访问!
  回复  引用  查看    

#45楼[楼主] 2009-03-16 08:43 Artech      

--引用--------------------------------------------------
sps.shareach.com: Artech
在PerSession时候, 服务端好像在客户端退出后不会自动销毁Session, 不知道怎么回事?
总体看懂了WCF Session,好文啊, 看了2天,终于到这了,不知道还要看多少天
--------------------------------------------------------
需要客户端显式调用Close方法终止Session。

对于时继承自ClientBase的ServiceProxy,直接调用Close方法;如果是通过CHannelFactory创建的Proxy,转换成ICommunicationObject后调用Close方法!
  回复  引用  查看    

#46楼[楼主] 2009-03-16 08:46 Artech      

--引用--------------------------------------------------
sps.shareach.com: 不知道客户端实例化代理类后长时间不操作, 对session影响是什么?
--------------------------------------------------------
如果客户端不调用Close将Session终止,服务端基于该Session的InstanceContext在超过规定的时限之前会一直存在。InstanceContext和被它直接或者间接引用的资源将会闲置,并不能被用于其它服务调用请求的处理。这是对资源的浪费,将会严重影响应用的可扩展性和吞吐量!
  回复  引用  查看    

#47楼 2009-03-16 10:31 sps.shareach.com      

@Artech
我的意思是即使ConcurrencyMode=ConcurrencyMode.Multiple
,InstanceContextMode为:
InstanceContextMode.PerCall那么他自动创建工作线程应该只有一个吧
InstanceContextMode.PerSession是否在客户端启动多线程后,服务端会是多线程处理吗?单线程会排队?
ProxyClassInstance.Close看来只能在超时上处理了, 因为客户端可能会因为网络等各种原因中断,我现在的处理模式是服务器端设置超时,客户端间隔内心跳2次, 是否这种超时可以,呵呵.
希望能出一些关于WCF性能和效率的专题, 以及企业内部和互联网各个应用场景的专题, 呵呵
你的文章是最好理解的,切入的深,但是理解的透, 比看其它blog和msdn效率高多了
  回复  引用  查看    

#48楼 2009-03-16 13:57 sps.shareach.com      

http://sps.shareach.com/blog/Lists/Posts/Post.aspx?ID=40" target="_new">http://sps.shareach.com/blog/Lists/Posts/Post.aspx?ID=40
我把你wcf文章转载列表了一下,不介意吧,呵呵
  回复  引用  查看    

#49楼[楼主] 2009-03-23 08:01 Artech      

--引用--------------------------------------------------
sps.shareach.com: @Artech
我的意思是即使ConcurrencyMode=ConcurrencyMode.Multiple
,InstanceContextMode为:
InstanceContextMode.PerCall那么他自动创建工作线程应该只有一个吧
InstanceContextMode.PerSession是否在客户端启动多线程后,服务端会是多线程处理吗?单线程会排队?
ProxyClassInstance.Close看来只能在超时上处理了, 因为客户端可能会因为网络等各种原因中断,我现在的处理模式是服务器端设置超时,客户端间隔内心跳2次, 是否这种超时可以,呵呵.
希望能出一些关于WCF性能和效率的专题, 以及企业内部和互联网各个应用场景的专题, 呵呵
你的文章是最好理解的,切入的深,但是理解的透, 比看其它blog和msdn效率高多了
--------------------------------------------------------
好的,等忙过了这阵子,我会试着写一些,谢谢关注!
  回复  引用  查看    

#50楼[楼主] 2009-03-23 08:01 Artech      

--引用--------------------------------------------------
sps.shareach.com: <a href="http://sps.shareach.com/blog/Lists/Posts/Post.aspx?ID=40" target="_new">http://sps.shareach.com/blog/Lists/Posts/Post.aspx?ID=40</a>
我把你wcf文章转载列表了一下,不介意吧,呵呵
--------------------------------------------------------
当然不介意,呵呵!
  回复  引用  查看    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 781216




相关文章:

相关链接: