服务行为 之 并发与实例化

本章节介绍 服务行为中的 并发与实例化行为,服务行为的介绍可以参考 《 WCF Behaviors(行为)


并发与实例化的引入,相关的名词解释

并发(Concurrency):对同时执行的任务数量,度量单位为任务(如请求、作业、事务等);

执行时间(Execution Time):完成任务所用的时间,度量单位为时间(如秒、毫秒等);

吞吐量(Throughput):固定时间内完成的任务数量,度量单位为 任务/时间 如:请求/秒、事务/分钟;

提高吞吐量的办法:

  1. 减少执行时间

      通过修改任务内部算法或增加硬件资源来完成,WCF在此方面作用有限。

  1. 提高并发

       通过提高单位时间内的并发数量, WCF中控制另发的两个行为:InstanceContextModeConcurrencyMode

 

ServiceBehavior 属性不定义在接口上,而是服务类上,因为ServiceBehavior属性针对的是服务的行为,而不是服务的契约。

InstanceContextMode 服务行为用于控制实例化,取值说明:

  1. Single : 所有入站请求均共用同一个服务类的实例;
  2. PerCall :为每一个入站请求创建一个服务类的实例;
  3. PerSession:为每一个会话创建一个服务类的实例,默认值;

InstanceContextMode 的默认值为PerSession,但当使用无会话的信道(通道)或者无会话绑定时,会变化为PerCall行为。

ConcurrencyMode 服务行为用于控制服务实例内的线程并发,取值说明:

Single:同一时刻仅能有一个线程访问服务类,默认值;

Reentrant:同一个是仅能有一个线程访问服务类,但该线程可以暂时离开服务类进入等待状态,而后又激活继续访问;

Multiple:多线程并发访问服务类。

Single 是最安全的设置,服务操作不需要担心线程安全问题,Multiple 需要类具有内置的线程安全能力。

同时使用 InstanceContextMode 与 ConcurrencyMode 这两个设置,可以配置服务的实例化和并发来满足特定的性能需求。

会话实例 - 单线程 - 默认-示例

定义服务接口

[ServiceContract]
    public interface IStockService
    {        
        [OperationContract]
        double GetPrice(string ticker);
    }

实现服务,输出线程ID

public class StockService : IStockService
    {
        public StockService()
        {
            Console.WriteLine("{0}:Created new instance of StockService on thread", DateTime.Now);
        }
      
        #region IStockService Members

        public double GetPrice(string ticker)
        {
            Console.WriteLine("{0}: GetPrice called on thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);

            return 94.95;
        }

        #endregion
    }

客户端异步调用代码

public static void GetPriceWithAsync(string address, Binding binding)
        {
            //IStockService proxy = CreateStockServiceProxy(address, binding);
            StockServiceClient proxy = new StockServiceClient(stockContext, binding, new EndpointAddress(address));

            IAsyncResult arGetPrice;
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("{0} : Calling GetPrice", DateTime.Now);
                arGetPrice = proxy.BeginGetPrice("msft", GetPriceCallback, proxy);
                Thread.Sleep(100);//为了让输出更清晰
                Interlocked.Increment(ref c);
            }
            while (c > 0)
            {
                Thread.Sleep(1000);
            }
            stockProxy.Close();
        }

        static void GetPriceCallback(IAsyncResult ar)
        {
            double price = ((StockServiceClient)ar.AsyncState).EndGetPrice(ar);
            Console.WriteLine("{0}: GetPrice result:{1}", DateTime.Now, price);
            Interlocked.Decrement(ref c);
        }

执行结果

默认并发-实例化-无会话结果

结果分析:

1. 对每个客户端的请求,服务端仅生成了一个服务实例

2. 每个请求都在各自的线程中被执行。

以上代码采用net.tcp方式进行绑定,net.tcp支持会话,因此InstanceContextMode取值为PerSession;

ConcurrencyMode.Single 设置使得每个WCF对于每个服务实例只允许一个线程。

多实例 - 单线程 - 示例

服务类代码:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class StockService : IStockService

执行结果

多实例-单线程-结果

结果分析:

1. 为每个请求创建了一个实例;

2. 每个请求在各自的线程中处理请求。

InstanceContextMode.PerSession 在绑定不支持会话的情况下会演变为 InstanceContextMode.PerCall ,故如果采用BasicHttpBinding方式,则无须进行设置。

单实例-多线程-示例

适用情况:

服务初始化代价高昂,如:构造方法需要从数据库中加载大量数据,或者构建一个大的数据结构;

服务代码中存在锁机制,可以保护线程局部安全,提高伸缩性;

服务类代码:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class StockService : IStockService

执行结果:

单实例-多线程结果

结果分析:

1. 服务端仅创建了一次实例;

2. 每次请求是在不同的线程中执行的;

单实例 - 单线程 - 单例模式

适用情况:没有锁机制,要求先进先出处理方式,调用者存在共享情况

服务端代码:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
    public class StockService : IStockService

执行结果:

单实例-单线程-结果

结果分析:

1. 与 会话实例 - 单线程 - 示例中不同的是,显式声明 InstanceContextMode = InstanceContextMode.Single 后,一旦host成功,自动会创建服务实例;

2. 多个请求重用一个线程,

ConcurrencyMode.Single 设置使得WCF在同一时刻只允许运行一个服务类的一个实例,并不控制创建线程的总量

 

会话级实例

要实现基于会话的服务实例,必须要实现两点:

1. 在契约级打开会话功能:SessionMode : Allowed|NotAllowed|Requied;

2. 在服务级设置按会话创建服务实例;

虽然会话是在契约级别设置是否允许,但其真正实现是在绑定元素指定的信道,因此服务第一次启动时会校验契约行为与信道的兼容性,如果信道需要会话,而绑定不支持会话(如:basicHttpBinding),则会抛出异常

会话实例 - 多线程 - 示例

代码:

[DataContract]
    public class StockPrice
    {
        [DataMember]
        public double Price;

        [DataMember]
        public int CallsCounter;
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class StockService : IStockService
    {        
        public StockService()
        {
            Console.WriteLine("{0}:Created new instance of StockService on thread", DateTime.Now);
        }
        
        public StockPrice GetStockPrice(string ticker)
        {
            StockPrice p = new StockPrice();
            Console.WriteLine("{0}: GetPrice called on thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
            
            p.Price = 94.85;
            lock (locker)
            {
                p.CallsCounter = ++callsCounter;
            }
            Thread.Sleep(1000);
            return p;
        }
    }

执行结果:启动两个客户端,同时调用

会话实例-多线程-结果

结果分析:

1. 创建了两个实例,分别针对两个客户端;

2. 计数器针对两个客户端是独立的,分别计数;

3. 服务端处理请求在3个线程中进行,属于多线程。

如果将InstanceContextMode改为Single,则计数器将从1增长到6

使用配置 控制 实例、线程

通过配置对程序中 的多实例、多线程、会话数进行控制

maxConcurrentInstances : 最大并发实例数,当InstanceContextMode取值为 PerSession或PerCall 时此配置会起作用,Single表示只存在一个实例;

maxConcurrentCalls:最大并发调用数量,当 ConcurrencyMode 为 Mutiple时起作用;

maxConcurrentSeesions:最大会话数量,当InstanceContextMode取值为PerSession时起作用,当达到最大数量时,会等待其他会话关闭。

posted @ 2011-09-27 22:26  JerryShi  阅读(695)  评论(0编辑  收藏  举报