前言
本次记录的主要内容为以下三点:会话、实例化、并发
第一部分--会话
会话是在两个终结点之间发送的所有消息的一种相互关系
在服务协定上设置System.ServiceModel.ServiceContractAttribute.SessionMode值
--Allowed:默认 客户端可以进行连接,并选择建立会话或不建立会话
--Required 所有调用(即,支持调用的基础消息交换)都必须是同一个对话的一部分
--NotAllowed 禁止会话
WCF会话具有下列主要概念性功能:
--他们由调用应用程序显式启动和终止。
--会话期间传递的消息按照接收消息的顺序进行处理。
--会话将一组消息相互关联,从而形成会话。该关联的含义是抽象的。例如,一个基于会话的通道可能会根据消息正文中的共享标记来关联消息。可以从会话派生的功能取决于关联的性质。
--不存在与WCF会话相关联的常规数据存储区。
ASP.NET会话和WCF会话的区别
--ASP.NET会话总是由服务器启动。
--ASP.NET会话原来本是无序的。
--ASP.NET会话提供了一种跨请求的常规数据存储机制。
客户端应用程序和服务应用程序以不同方式与会话交互。
--客户端应用程序启动会话,然后接收并处理在该会话内发送的消息。
--服务应用程序可以将会话用作扩展点,以添加其他行为。通过直接使用InstanceContext或实现一个自定义实例上下文提供程序,可以做到这一点
当WCF服务接受客户端会话时,默认情况下启用以下功能:
--通过同一用户定义的服务对象来处理WCF客户端对象之间的所有调用。
--除了此实例化行为之外,不同的基于会话的绑定还会提供其他功能。
系统提供的会话类型
--System.ServiceModel.ChannelsSecurityBindingElement支持基于安全的会话,其中,两个通信端采用统一的安全对话。
--System.ServiceModel.WSHttpBinding绑定(包含对安全会话和可靠会话的支持)默认情况下只使用对消息进行加密和数字签名的安全会话。
--System.ServiceModel.NetTcpBinding绑定支持基于TCP/IP的会话,以确保所有消息都由套接字级别的连接进行关联。
--System.ServiceModel.Channels.ReliableSessionBindingElement元素实现WS-ReliableMessaging规范,并提供对可靠会话的支持。在可靠会话中,可以配置消息以按顺序传递并且只传递一次,从而使消息在对话期间即使经过多个节点也可以确保保密性。
第二部分--会话实例
先来看服务接口
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)] public interface ICalculatorSession { [OperationContract(IsOneWay=true)] void Clear(); [OperationContract(IsOneWay = true)] void AddTo(double n); [OperationContract(IsOneWay = true)] void SubtractFrom(double n); [OperationContract(IsOneWay = true)] void MultiplyBy(double n); [OperationContract(IsOneWay = true)] void DivideBy(double n); [OperationContract] double Result(); }
此处的会话设置模型为Required
接下来看一下服务实现
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] public class CalculatorService : ICalculatorSession { double result = 0.0D; public void Clear() { result = 0.0D; } public void AddTo(double n) { result += n; } public void SubtractFrom(double n) { result -= n; } public void MultiplyBy(double n) { result *= n; } public void DivideBy(double n) { result /= n; } public double Result() { return result; } }
InstanceContextMode可用来处理包含在传入消息中的调用的服务实例数。
// 摘要: // 为每个会话创建一个新的 System.ServiceModel.InstanceContext 对象。 PerSession = 0, // // 摘要: // 新的 System.ServiceModel.InstanceContext 对象在每次调用前创建,在调用后回收。如果信道未创建会话,则该值的行为就如同 // System.ServiceModel.InstanceContextMode.PerCall 一样。 PerCall = 1, // // 摘要: // 只有一个 System.ServiceModel.InstanceContext 对象用于所有传入呼叫,并且在调用后不回收。如果服务对象不存在,则创建一个。 Single = 2,
我这里是通过IIS进行承载服务的,然后通过svcutil.exe生成客户端代理类和配置文件。
下面再来看一下客户端是如何调用的
CalculatorSessionClient client = new CalculatorSessionClient(); client.Clear(); client.AddTo(100.0D); client.SubtractFrom(50.0D); client.MultiplyBy(17.65D); client.DivideBy(2.0D); double result = client.Result(); Console.WriteLine("(((0 + 100) - 50) * 17.65) / 2 = {0}", result); //Closing the client gracefully closes the connection and cleans up resources client.Close(); Console.WriteLine(); Console.WriteLine("Press <ENTER> to terminate client."); Console.ReadLine(); }
因为我们在服务实现类设置的为[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
如果设置为[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)],结果如下,那么就是每次调用方法的时候都会创建一个服务实例
这是关于会话的代码示例会话代码实例
第二部分--实例化
实例化行为(使用System.ServiceModel.ServiceBehaviorAttribute.InstanceContextModel属性进行设置)控制如何创建InstanceContext以响应传入的消息。
默认情况下,每个InstanceContext都与一个用户定义服务对象相关联,因此(在默认情况下)设置InstanceContextMode属性也可以控制用户定义服务对象的实例化
可以使用下列实例化模式:
PerCall:为每个客户端请求创建一个新的InstanceContext(以及相应的服务对象)。
Perseesion:为每个新的客户端会话创建一个新的InstanceContext(以及相应的服务对象),并在该会话的生存期内对其进行维护(这需要使用支持会话的绑定)。
Single:单个InstanceContext(以及相应的服务对象)处理应用程序生存期内的所有客户端请求。
第二部分--实例化实例
首先来看一下加减乘除的服务契约
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)] public interface ICalculator { [OperationContract] double Add(double n1, double n2); [OperationContract] double Subtract(double n1, double n2); [OperationContract] double Multiply(double n1, double n2); [OperationContract] double Divide(double n1, double n2); }
接下来是三个获取服务设置的服务契约,并继承自上面的加减乘除的服务契约
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)] public interface ICalculatorInstance : ICalculator { [OperationContract] string GetInstanceContextMode(); [OperationContract] int GetInstanceId(); [OperationContract] int GetOperationCount(); }
接下来再一下服务实现
public class CalculatorService : ICalculatorInstance { static Object syncObject = new object(); static int instanceCount; int instanceId; int operationCount; public CalculatorService() { lock (syncObject) { instanceCount++; instanceId = instanceCount; } } public double Add(double n1, double n2) { operationCount++; return n1 + n2; } public double Subtract(double n1, double n2) { Interlocked.Increment(ref operationCount); return n1 - n2; } public double Multiply(double n1, double n2) { Interlocked.Increment(ref operationCount); return n1 * n2; } public double Divide(double n1, double n2) { Interlocked.Increment(ref operationCount); return n1 / n2; } public string GetInstanceContextMode() { // Return the InstanceContextMode of the service ServiceHost host = (ServiceHost)OperationContext.Current.Host; ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>(); return behavior.InstanceContextMode.ToString(); } public int GetInstanceId() { // Return the id for this instance return instanceId; } public int GetOperationCount() { // Return the number of ICalculator operations performed on this instance lock (syncObject) { return operationCount; } }
可以发现服务实现中有一个构造函数,此构造函数便是用来检测服务实例数目。
服务实现中的GetInstanceContextMode是用来获取当前服务的实例模式。
GetOperationCount是用来返回在服务中Operation的次数
最后还是通过IIS来承载WCF服务,并通过svcutil.exe来获取当前服务的客户端代理类和客户端文件配置
通过上面代码可见InstanceContextMode = InstanceContextMode.Single
然后我们来看一下客户端调用代码
static void Main() { // Create a client //首先实例化一个服务代理类 CalculatorInstanceClient client = new CalculatorInstanceClient(); ///调用服务端GetInstanceContextMode来获取服务端服务模式 string instanceMode = client.GetInstanceContextMode(); Console.WriteLine("InstanceContextMode: {0}", instanceMode); ///调用操作计算 DoCalculations(client); // Create a second client //然后又实例化了一个客户端代理类 CalculatorInstanceClient client2 = new CalculatorInstanceClient(); //进行第二次的操作计算的调用 DoCalculations(client2); //Closing the client gracefully closes the connection and cleans up resources client.Close(); Console.WriteLine(); Console.WriteLine("Press <ENTER> to terminate client."); Console.ReadLine(); } static void DoCalculations(CalculatorInstanceClient client) { // Call the Add service operation. double value1 = 100.00D; double value2 = 15.99D; double result = client.Add(value1, value2); Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result); Console.Write("InstanceId: {0}", client.GetInstanceId()); Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount()); // Call the Subtract service operation. value1 = 145.00D; value2 = 76.54D; result = client.Subtract(value1, value2); Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result); Console.Write("InstanceId: {0}", client.GetInstanceId()); Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount()); // Call the Multiply service operation. value1 = 9.00D; value2 = 81.25D; result = client.Multiply(value1, value2); Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result); Console.Write("InstanceId: {0}", client.GetInstanceId()); Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount()); // Call the Divide service operation. value1 = 22.00D; value2 = 7.00D; result = client.Divide(value1, value2); Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result); Console.Write("InstanceId: {0}", client.GetInstanceId()); Console.WriteLine(" , OperationCount: {0}", client.GetOperationCount()); }
执行结果如下
服务模式为Single
InstanceId始终为1,即表达Single的作用
OperationCount为递增到88次调用结果当然为8.
可以修改成为另外两种模式,进行测试查看效果。
第三部分--并发
并发是对InstanceContext中的任一时刻处于活动状态的线程数量的控制。
此控制是通过将System.ServiceModel.ServiceBehaviorAttribute.ConcurrencyModel与ConcurrencyMode枚举结合使用来实现的。
有一下三种可用的并发模式:
Single:最多允许每个实例上下文同事有用一个对该实例上下文中的消息进行处理的线程。其他希望使用同一个实例上下文的线程必须一直阻塞,直到原始线程退出该实例上下文为止。
Multiple:每个服务实例都可以拥有多个同时处理消息的线程。若要使用此并发模式,服务实现必须是线程安全的。
Reentrant:每个服务实例一次只能处理一个消息,但可以接受可重入的操作调用。仅当服务通过WCF客户端对象提供服务时才会接受这些调用。
并发的使用与实例化模式有关。在PerCall实例化过程中,与并发没有关系,因为每个消息都由一个新的InstanceContext处理,因此InstanceContext中处于活动状态的线程永远都不会超过一个。
第三部分--并发实例
第一来看一下服务契约的定义
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")] public interface ICalculator { [OperationContract] double Add(double n1, double n2); [OperationContract] double Subtract(double n1, double n2); [OperationContract] double Multiply(double n1, double n2); [OperationContract] double Divide(double n1, double n2); } // Define a service contract to inspect concurrency state [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")] public interface ICalculatorConcurrency : ICalculator { [OperationContract] string GetConcurrencyMode(); [OperationContract] int GetOperationCount(); }
第二看一下服务的实现
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single)] // Uses Thread.Sleep to vary the execution time of each operation public class CalculatorService : ICalculatorConcurrency { int operationCount; public double Add(double n1, double n2) { operationCount++; System.Threading.Thread.Sleep(180); return n1 + n2; } public double Subtract(double n1, double n2) { operationCount++; System.Threading.Thread.Sleep(100); return n1 - n2; } public double Multiply(double n1, double n2) { operationCount++; System.Threading.Thread.Sleep(150); return n1 * n2; } public double Divide(double n1, double n2) { operationCount++; System.Threading.Thread.Sleep(120); return n1 / n2; } public string GetConcurrencyMode() { // Return the ConcurrencyMode of the service ServiceHost host = (ServiceHost)OperationContext.Current.Host; ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>(); return behavior.ConcurrencyMode.ToString(); } public int GetOperationCount() { // Return number of operations return operationCount; } }
第三 客户端调用
static void Main() { // Create a client CalculatorConcurrencyClient client = new CalculatorConcurrencyClient(); Console.WriteLine("Press <ENTER> to terminate client once the output is displayed."); Console.WriteLine(); // Communicate with service using asynchronous methods. client.BeginGetConcurrencyMode(GetConcurrencyModeCallback, client); // BeginAdd double value1 = 100.00D; double value2 = 15.99D; IAsyncResult arAdd = client.BeginAdd(value1, value2, AddCallback, client); Console.WriteLine("Add({0},{1})", value1, value2); // BeginSubtract value1 = 145.00D; value2 = 76.54D; IAsyncResult arSubtract = client.BeginSubtract(value1, value2, SubtractCallback, client); Console.WriteLine("Subtract({0},{1})", value1, value2); // BeginMultiply value1 = 9.00D; value2 = 81.25D; IAsyncResult arMultiply = client.BeginMultiply(value1, value2, MultiplyCallback, client); Console.WriteLine("Multiply({0},{1})", value1, value2); // BeginDivide value1 = 22.00D; value2 = 7.00D; IAsyncResult arDivide = client.BeginDivide(value1, value2, DivideCallback, client); Console.WriteLine("Divide({0},{1})", value1, value2); client.BeginGetOperationCount(GetOperationCountCallback, client); Console.ReadLine(); //Closing the client gracefully closes the connection and cleans up resources client.Close(); } // Asynchronous callbacks for displaying results. static void GetConcurrencyModeCallback(IAsyncResult ar) { string result = ((CalculatorConcurrencyClient)ar.AsyncState).EndGetConcurrencyMode(ar); Console.WriteLine("ConcurrencyMode : {0}", result); } static void GetOperationCountCallback(IAsyncResult ar) { int result = ((CalculatorConcurrencyClient)ar.AsyncState).EndGetOperationCount(ar); Console.WriteLine("OperationCount : {0}", result); } static void AddCallback(IAsyncResult ar) { double result = ((CalculatorConcurrencyClient)ar.AsyncState).EndAdd(ar); Console.WriteLine("Add Result : " + result); } static void SubtractCallback(IAsyncResult ar) { double result = ((CalculatorConcurrencyClient)ar.AsyncState).EndSubtract(ar); Console.WriteLine("Subtract Result : " + result); } static void MultiplyCallback(IAsyncResult ar) { double result = ((CalculatorConcurrencyClient)ar.AsyncState).EndMultiply(ar); Console.WriteLine("Multiply Result : " + result); } static void DivideCallback(IAsyncResult ar) { double result = ((CalculatorConcurrencyClient)ar.AsyncState).EndDivide(ar); Console.WriteLine("Divide Result : " + result); }
会话与InstanceContext设置进行交互
SessionMode属性值和InstanceContextMode属性值组合的情况下,支持会话或不支持会话的传入通道的结果。