为什么我的应用程序如此等待同步?

时间:2012-06-27 07:21:58

标签: c# .net wcf sql-server-2008 concurrency

我正在进行一项性能测试,该测试涉及多个客户,每个客户尽可能快地轰炸服务器150个请求。

服务器由3个WCF服务构成,一个通过httpbinding向外部打开,它通过net.pipe(IPC)与另外两个服务进行通信。其中一项服务负责数据库连接(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%的时间浪费在“等待同步 < / p>

当我打开调用图时,我发现两件看起来很奇怪的东西,但我不确定它们是什么意思:

  1. System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke正在树的顶部使用,是否可以进行并发处理?
  2. 所有同步问题都涉及某种SQL Server活动,如ExecuteNonQuery,ExecuteReader等(当我到达调用树的底部时)
  3. 我注意到数据库连接服务使用的是DAL项目(遗憾的是一些遗留代码),这是完全静态的。

    阅读this之后我不确定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后,我注意到仍然没有变化(至少在数据库服务中)。我看到的是主入口点服务可能会打开很多线程,但只有少数(仍然是大约8-10个线程)在数据库连接服务上打开。

    还有其他原因导致这种情况发生吗?我排除了DAL是一个问题,因为没有足够的请求进入数据库服务,所以我在服务中找到它的东西或客户端中的东西......

    第3次编辑:

    以下是配置文件:

    经理的配置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的几乎相同。

    所有服务都是自托管的,我在Manager和Business代码中有一个DBConnectionProxy类,它们按如下方式激活:

     DBConnectionContractClient _dbConnectionContractClient = null;
            try
            {
                objDBConnectionContractClient = new DBConnectionContractClient();
                objDBConnectionContractClient.ExecuteStoredProcedure(input, out result);
            }
    

3 个答案:

答案 0 :(得分:4)

PerCall在这种情况下,您可以考虑这种实例化模式。

  • 如果您的服务是无国籍的

  • 如果您的服务具有轻量级初始化代码(或者在 全部)。

  • 如果您的服务是单线程的。

一些很好的教程。请参阅有关调整的第三个链接。

WCF Instancing, Concurrency, and Throttling – Part 1

WCF Instancing, Concurrency, and Throttling – Part 2

WCF Instancing, Concurrency, and Throttling – Part 3

答案 1 :(得分:2)

更喜欢每次通话。如果要最小化初始化资源密集型对象或为每个请求运行某些预处理路由或想要维护状态,则会话将非常有用。会话可以为您提供更好的性能,但每次呼在每次通话中,您不必担心可靠性。而persession / multiple的缺点是你必须始终考虑代码中的线程安全性。

答案 2 :(得分:1)

如果您不关心服务状态,并且如果在初始化时没有进行繁重的工作,那么最佳选择是每次调用,我假设它描述了您的情况。尝试删除这些DAL电话并查看您的服务行为,这样您就可以确定这是否是您的瓶颈。