默认情况下,WCF 宿主将会启动尽可能多的实例来处理请求消息。如果一个服务的实例和并发行为并没有确定,WCF将会为每个进入的请求创建一个服务实例同时将按照需要分配线程来反应请求。总体来说这是一个对性能和扩展性都很好的解决方案因为服务将会最大化扩展硬件性能。
但是有很多情况你可能想舍弃使用这种行为。为了这个目的,有三种在并发和实例上的设置可以实现。这些定义在配置文件的行为组分的serviceThrottling元素里。
maxConcurrentInstances行为控制有多少个服务实例可以被一个服务创建。这个设置是有用的如果ConcurrencyMode是PerCall或者PerSession,因为这两个设置都可以指导WCF按照要求创建实例。通过定义WCF可以创建的实例的最大数目,你设置的实例数目的上限将会存储在内存中。当限制达到时,不会有更多的实例被创建除非其他实例被回收或重用。
列表5.6显示了一个没有确定ConcurrencyMode和InstancingMode的服务,这意味着将会使用默认值Single和PerSession.服务操作花费20秒完成。
列表5.6 使用默认并发和实例行为的服务
[ServiceContract]
public interface IStockService
{
[OperationContract]
double GetPrice(string ticker);
}
public class StockService : IStockService
{
StockService()
{
Console.WriteLine("{0}:Created new instance of StockService on thread", DateTime.Now);
}
public StockPrice GetPrice(string ticker)
{
Console.WriteLine("{0}: GetPrice called on thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
return 94.85;
}
}
列表5.7 显示了一个异步调用服务端10次的客户端
class Program
{
static int c = 0;
static void Main(string[] args)
{
Console.WriteLine();
StockServiceClient p = new StockServiceClient();
for (int i = 0; i < 10; i++)
{
Console.WriteLine("{0}: Calling GetPrice", DateTime.Now);
p.BeginGetPrice("MSFT", GetPriceCallback, p);
Interlocked.Increment(ref c);
}
while (c > 0) //wait until all responses come back
{
Thread.Sleep(100);
}
Console.ReadLine();
}
static void GetPriceCallback(IAsyncResult ar)
{
try
{
StockPrice p = ((StockServiceClient)ar.AsyncState).EndGetPrice(ar);
Console.WriteLine("{0}: Price: {1}", DateTime.Now, p.price);
((StockServiceClient)ar.AsyncState).Close();
Interlocked.Decrement(ref c);
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException.Message);
}
}
}
列表5.8 显示了服务端的app.config文件。maxConcurrentInstances行为被设置成5,指示在服务端不会创建多于5个实例。
列表5.8 使用maxConcurrentInstances限制并发数量
图片5.6显示了客户端(左边)输出和服务端(右边)输出。在客户端,注意当程序启动时10次调用是如何立即执行的,然后5个结果在20秒钟后返回而另外5个结果在另外20秒钟后返回。在服务端输出,注意前5个实例是如何在客户端请求到来时立即创建的,但是另外5个实例没有创建直到前5个实例关闭了以后。
图片5.6 控制并发实例的输出