我正在进行一项性能测试,其中涉及多个客户端以尽可能快的速度轰炸服务器,每个客户端发出 150 个请求。
服务器由 3 个 WCF 服务构成,其中一个通过 httpbinding 对外开放,它通过 net.pipe (IPC) 与其他 2 个服务通信。其中一项服务负责数据库连接(SQL server 2008 R2)。
此数据库连接服务使用以下连接字符串增强功能:
Min Pool Size=20; Max Pool Size=1000; Connection Timeout=20;
并且受到 WCF 限制(与所有其他 WCF 服务一样)。
我注意到当我激活 1 个客户端时可能需要 3 秒,但是当我激活 3 个客户端时可能需要 8-9 或更多。
我使用 SQL 服务器分析器检查了使用了多少并发进程,我看到只有大约 8 个进程正在使用。
所以我意识到在服务器的某个地方,请求被排队而不是同时处理。
为了深入了解它,我使用了性能分析器(确切地说是 ANTS),它告诉我大约 70% 的时间浪费在“等待同步”上
当我打开调用图时,我发现有两件事看起来很奇怪,但我不确定它们的含义:
- System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke 正在树的顶部使用,这可以用于并发处理吗?
- 所有同步问题都涉及某种 SQL Server 活动,例如 ExecuteNonQuery、ExecuteReader 等(当我到达调用树的底部时)
我注意到数据库连接服务使用了一个完全静态的 DAL 项目(不幸的是一些遗留代码)。
读完后我不确定 DAL 的代码是否有问题,这里是一个存储过程调用的示例。
public static int PerformStoredProcedure(string storedP,string ext,out string msg)
{
msg = "";
SqlCommand command = GetSqlCommand(storedP,ext);
command.Connection.Open();
int result = (int)PerformStoredProcedure(command,out msg);
command.Connection.Close();
return result;
}
此方法通常从数据库连接服务调用:
public static int PerformStoredProcedureWithParams(string storedP,string ext,out string msg, params object[] pars)
{
msg = "";
SqlCommand command = GetSqlCommand(storedP,ext);
UpdateCommandParams(command, pars);
command.Connection.Open();
int result = (int)PerformStoredProcedure(command,out msg);
command.Connection.Close();
return result;
}
那么,这里有什么问题吗?
或者也许我应该看看别的地方?
编辑:
在 Brijesh 发表评论后,我意识到我没有更改 WCF 服务的默认 InstanceContextMode 和 ConcurrencyMode ......我猜这是初学者的错误。
我仍然不确定我应该使用 PerSession/Multiple 还是 PerCall/Single。正如我所看到的,无论客户端如何,每个服务都应该将每个请求作为对象处理。
我应该使用什么?
第二次编辑:
使用 PerCall 和 PerSession/Multiple 后,我注意到仍然没有变化(至少在 DB 服务中)。我看到的是主入口点服务可能会打开很多线程,但在 DB 连接服务上只打开了几个(仍然是大约 8-10 个线程)。
还有其他原因导致这种情况发生吗?我排除了 DAL 是一个问题,因为数据库服务中没有足够的请求,所以我认为它在服务中的某些东西或客户端中的某些东西......
第三次编辑:
以下是配置文件:
经理的配置 wcf 服务部分:
<services>
<service behaviorConfiguration="ServicesBehavior" name="Verifone.GenericPP.GPPManagerService.GPPManagerServiceImpl">
<host>
<baseAddresses>
<add baseAddress="http://localhost:9090/GPPManagerService/"/>
</baseAddresses>
</host>
<endpoint contract="Verifone.GenericPP.GPPManagerService.IGPPManagerService" binding="basicHttpBinding" address="GPPManagerService"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServicesBehavior">
<!--amith 13-05-2012-->
<serviceThrottling
maxConcurrentCalls="1000"
maxConcurrentSessions="1000"
maxConcurrentInstances="1000"
/>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBinding" maxBufferSize="10000000" maxReceivedMessageSize="10000000">
<readerQuotas maxStringContentLength="10000000" maxArrayLength="10000000"/>
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</binding>
经理的客户:
<endpoint name="endpoint1" contract="IDBConnectionContract" bindingConfiguration="basicHttpBinding" binding="basicHttpBinding" address="http://localhost:9010/DBConnectionService/DBService"></endpoint>
<endpoint name="endpoint2" contract="IGPPService" bindingConfiguration="basicHttpBinding" binding="basicHttpBinding" address="http://localhost:9095/GenericPPService/GenericPPService"></endpoint>
数据库连接服务:
<service behaviorConfiguration="ServicesBehavior" name="Verifone.DBConnectionService.DBConnectionContracImpl">
<host>
<baseAddresses>
<add baseAddress="http://localhost:9010/DBConnectionService/"/>
<add baseAddress="net.pipe://localhost/DBConnectionService/"/>
</baseAddresses>
</host>
<endpoint contract="Verifone.DBConnectionService.IDBConnectionContract" binding="basicHttpBinding" address="DBService"></endpoint>
<endpoint contract="Verifone.DBConnectionService.IDBConnectionContract" binding="netNamedPipeBinding" bindingConfiguration="NetNamedPipeBinding_Configuration" address="" name="pipeEndpoint"/>
</service>
业务逻辑服务的客户端与 Manager 的客户端几乎相同。
所有服务都是自托管的,我在经理和业务代码中有一个 DBConnectionProxy 类,它们像这样激活:
DBConnectionContractClient _dbConnectionContractClient = null;
try
{
objDBConnectionContractClient = new DBConnectionContractClient();
objDBConnectionContractClient.ExecuteStoredProcedure(input, out result);
}