您的位置:首页 > 其它

WCF学习之:实例上下文模式和并发模式的性能影响

2009-09-23 14:36 411 查看
实例上下文模式和并发模式的性能影响

Windows Communication Foundation (WCF) 应用程序的性能取决于许多不同的因素。应用程序设计、实例化模式、并发模式、传输、绑定设置和限制设置都会影响应用程序的性能。InstanceContextModeConcurrencyMode 设置彼此交互,对 WCF 应用程序性能会有很大影响。本主题讨论这两个设置如何影响 WCF 服务性能。在讨论这两个设置如何影响 WCF 之前,先简单说明一下其中每个设置的作用。

实例上下文模式

InstanceContextMode 控制在响应客户端调用时,如何分配服务实例。InstanceContextMode 可以设置为以下值:

Single - 为所有客户端调用分配一个服务实例。

PerCall – 为每个客户端调用分配一个服务实例。

PerSession – 为每个客户端会话分配一个服务实例。

InstanceContextMode 的默认设置为 PerSession

有关 InstanceContextMode 的更多信息,请参见:

InstanceContextMode 枚举

MSDN 杂志主页,2008 年 6 月版(可能为英文网页)

如何:控制服务实例化

并发模式

ConcurrencyMode 控制一次允许多少个线程进入服务。ConcurrencyMode 可以设置为以下值之一:

Single - 一次可以有一个线程进入服务。

Reentrant - 一次可以有一个线程进入服务,但允许回调。

Multiple - 一次可以有多个线程进入服务。

ConcurrencyMode 的默认设置为 Single。默认选择此值是为了让开发人员无需考虑同步问题。有关 ConcurrencyMode 的更多信息,请参见:ConcurrencyMode

实例上下文模式、并发模式和性能

InstanceContextModeConcurrencyMode 设置可以相互影响,因此了解它们如何影响性能时,必须同时查看这两个设置。例如,将 InstanceContextMode 设置为 PerCall 时,会忽略 ConcurrencyMode 设置。这是因为,每个客户端调用都将路由到新的服务实例,因此一次只会有一个线程在服务实例中运行。将 InstanceContextMode 设置为 Single 时,仅创建一个服务实例,因此 ConcurrencyMode 会影响应用程序的吞吐量。

InstanceContextMode 设置为 Single 的原因很多,其中包括:

创建服务实例需要大量的处理工作。当多个客户端访问服务时,仅允许创建一个服务实例可以降低所需处理量。

一个服务实例可以创建多个对象。将 ConcurrencyMode 设置为 Single 可以降低垃圾回收成本,因为不必为每个调用创建和销毁服务创建的对象。

通过将 ConcurrencyMode 设置为 Single,可以在多个客户端之间共享服务实例。

如果 ConcurrencyModeInstanceContextMode 均设置为 Single,则一次只允许一个客户端调用通过。如果有大量客户端,这可能会导致较大的瓶颈。

InstanceContextMode 设置为 Single 并将 ConcurrencyMode 设置为 Reentrant 的性能特征与将 ConcurrencyMode 设置为 Single 时相同。这是因为,一次仅允许一个客户端线程进入服务。

InstanceContextMode 设置为 PerCall 时,将为每个客户端调用创建一个新服务实例上下文(默认情况下,这表示一个新服务实例),并在客户端调用完成时销毁该实例。由于服务实例仅在进行调用时可 用,因此在调用完成时,会释放它们可能访问的所有资源。由于为每个调用分配一个新服务实例,因此会产生一些开销。但是,由于每个客户端都获取自己的服务实 例,因此不存在同步问题。当大量客户端调用同时发生时,将创建大量的服务实例。遇此情形时,务必使服务实例仅分配其正常工作所需的那些资源。

如果 ConcurrencyMode 设置为 Multiple,则多个客户端调用可以通过,但开发人员需负责手动同步对共享数据的所有访问。这意味着,一次只有一个线程可以访问共享数据,从而导致访问共享数据的所有调用顺序排队等候。这违背了将 ConcurrencyMode 设置为 Multiple 的初衷。

InstanceContextMode 设置为 PerCall 时,ConcurrencyMode 设置对吞吐量没有影响。每个客户端调用都获取自己的服务实例,因此每个服务实例只有一个调用到其中的线程。在 InstanceContextMode 设置为 PerSession 时,每个会话都获取自己的服务实例。有关 会话的更多信息,请参见使用会话(可能为英文网页)。使用 PerSession 时,必须使用支持会话的绑定。下表显示系统提供的哪些绑定支持会话。默认会话设置括在圆括号中。

绑定会话(默认)
BasicHttpBinding

(无)

WSHttpBinding

无、可靠会话、(安全会话)

WSDualHttpBinding

(可靠会话)、安全会话

WSFederationHttpBinding

(无)、可靠会话、安全会话

NetTcpBinding

(传输)、可靠会话、安全会话

NetNamedPipeBinding

无、(传输)

NetMsmqBinding

(无)、传输

NetPeerTcpBinding

(无)

MsmqIntegrationBinding

(无)

BasicHttpContextBinding

(无)

NetTcpContextBinding

(传输)、可靠会话、安全会话

WSHttpContextBinding

无、可靠会话、(安全会话)

有关 系统定义的哪些绑定支持会话的更多信息,请参见系统提供的绑定配置系统提供的绑定。使用会话时,默认情况下每个客户端代理都获取自己的专用服务实例。来自该代理的所有调用都由同一服务实例处理。单个客户端可以创建两个不同的代理,在这种情况下将创建两个服务实例。两个代理可以共享同一会话。有关更多信息,请参见 ,请参见 InstanceContextSharing 示例。

如果 InstanceContextMode 设置为 PerSessionConcurrencyMode 设置为 Single,则每个代理都获取自己的服务实例。由于一次仅允许一个线程进入服务实例,因此单个代理发出的对服务实例的所有并发调用将顺序排队等候。由于一次仅允许一个线程进入服务实例,因此在该线程退出之前,所有其他调用都将阻塞。如果有大量客户端同时发出调用,这将导致瓶颈。

如果 InstanceContextMode 设置为 PerSessionConcurrencyMode 设置为 Multiple,则每个代理都获取自己的服务实例,且允许并发调用进入服务实例。因此,开发人员必须手动同步对共享数据的所有访问。同样,这会导致对共享数据的访问顺序排队等候,并降低性能,其原因与将 ConcurrencyMode 设置为 Single 时相同。

对于轻型服务实例,SinglePerCall 实例上下文模式之间的吞吐量差异不是很大(约为 8%)。

性能数据和测试

用 于收集本文档中讨论的性能数据的计算机配置为:一台服务器和四台客户端计算机,它们通过两个 1 Gbps 以太网网络接口连接。服务器为四处理器的 AMD 64 2.2 GHz 计算机,运行 Windows Server 2008。每台客户端计算机都是双处理器 AMD 2.2GHz x86 计算机,运行的操作系统与服务器相同。系统 CPU 利用率保持在接近 100%。

四台客户端计算机用于向 WCF 服务发出调用。向服务发出的每个调用将 1 到 100 个对象传递给服务操作,具体取决于正在执行的测试。每个对象都包含一个订单、有关采购者的信息和要采购的项。

测 试重点是 WCF 技术的服务器吞吐量。这定义为每秒完成的操作数。一个操作是一个请求和答复消息,服务几乎不进行处理。一个要了解的重点是,业务逻辑决定构造良好的 SOA 解决方案中的服务成本。通过省去服务上的业务逻辑处理,仅度量消息传递基础结构的成本。有些测试要求实现服务的类执行某些其他处理。对于这些测试,在服务 类的构造函数中放置了一些逻辑,以生成一组质数。每个测试运行 3 次,然后计算吞吐量的平均值。

下面的代码演示由用于生成性能数据的 WCF 服务实现的接口。

[ServiceContract]

public interface ITestContract

{

[OperationContract]

Order[] GetOrders(int NumOrders);

}

[DataContract]

public class OrderLine

{

[DataMember]

public int ItemID;

[DataMember]

public int Quantity;

}

[DataContract]

public class Order

{

public Order()

{

}

[DataMember]

public int CustomerID;

[DataMember]

public string ShippingAddress1;

[DataMember]

public string ShippingAddress2;

[DataMember]

public string ShippingCity;

[DataMember]

public string ShippingState;

[DataMember]

public string ShippingZip;

[DataMember]

public string ShippingCountry;

[DataMember]

public string ShipType;

[DataMember]

public OrderLine[] orderItems;

[DataMember]

public string CreditCardType;

[DataMember]

public string CreditCardNumber;

[DataMember]

public DateTime CreditCardExpiration;

[DataMember]

public string CreditCardName;

}

}


下面的代码演示服务的实现。

[ServiceBehavior]

public class LightWeightService : ITestContract

{

static Order[] orders;

int upperLimit = 10000;

static bool CtorWork = false;

public LightWeightService()

{

if (CtorWork)

DoSomething();

}

void DoSomething()

{

List<int> primes = new List<int>();

int j;

int last = -1;

int lastPrimeSquare = 0;

for (int i = 2; i < upperLimit; i++)

{

for (j = 0; j <= last; j++)

{

if (i % primes[j] == 0)

{

break;

}

}

if (j > last)

{

primes.Add(i);

if (i > lastPrimeSquare)

{

last++;

lastPrimeSquare = primes[last] * primes[last];

}

}

}

primes.Clear();

primes = null;

}

public static void Initialize(int messageSize)

{

orders = new Order[messageSize];

for (int i = 0; i < messageSize; i++)

{

Order order = new Order();

OrderLine[] lines = new OrderLine[2];

lines[0] = new OrderLine();

lines[0].ItemID = 1;

lines[0].Quantity = 10;

lines[1] = new OrderLine();

lines[1].ItemID = 2;

lines[1].Quantity = 5;

order.orderItems = lines;

order.CustomerID = 100;

order.ShippingAddress1 = "012345678901234567890123456789";

order.ShippingAddress2 = "012345678901234567890123456789";

order.ShippingCity = "0123456789";

order.ShippingState = "0123456789012345";

order.ShippingZip = "12345-1234";

order.ShippingCountry = "United States";

order.ShipType = "UPS";

order.CreditCardType = "VISA";

order.CreditCardNumber = "0123456789012345";

order.CreditCardExpiration = DateTime.UtcNow;

order.CreditCardName = "01234567890123456789";

orders[i] = order;

}

}

public Order[] GetOrders(int NumOrders)

{

return orders;

}

}


服务自承载在控制台应用程序中。下面的代码演示如何承载服务。

public static void Main(string[] args)

{

LightWeightService.Initialize(100);

WSHttpBinding binding = new WSHttpBinding(SecurityMode.None, false);

((WSHttpBinding)binding).MaxReceivedMessageSize = 2 * 1024 * 1024;

((WSHttpBinding)binding).ReaderQuotas.MaxArrayLength = 2 * 1024 * 1024;

ServiceHost serviceHost = new ServiceHost(typeof(LightWeightService));

ServiceBehaviorAttribute sba = serviceHost.Description.Behaviors.Find<ServiceBehaviorAttribute>();

if (sba == null)

{

sba = new ServiceBehaviorAttribute();

}

sba.ConcurrencyMode = ConcurrencyMode.Single;

sba.InstanceContextMode = InstanceContextMode.Single;

serviceHost.AddServiceEndpoint(typeof(ITestContract), binding, "http://localhost:8000/PerfTest");

serviceHost.Open();

Console.WriteLine("Service is running...");

Console.WriteLine("Press Enter to terminate service");

Console.ReadLine();

}


客户端应用程序也是一个控制台应用程序。下面的代码演示如何实现客户端。

static void Main(string[] args)

{

WSHttpBinding binding;

ITestContract proxy;

ChannelFactory<ITestContract> factory;

binding = new WSHttpBinding(SecurityMode.None, false);

factory = new ChannelFactory<ITestContract>(binding, new EndpointAddress("http://localhost:8000/PerfTest"));

proxy = factory.CreateChannel();

proxy.GetOrders(100);

}


文章来自微软MSDN: http://msdn.microsoft.com/zh-cn/library/cc681240.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐