【问题标题】:WCF throughput lower than expectedWCF 吞吐量低于预期
【发布时间】:2012-07-17 05:12:17
【问题描述】:

对于帖子的长度,我深表歉意,但有很多事情可能会导致我的情况,我已尝试将我根据其他帖子所做的所有设置更改包括在内。简而言之,我的 WCF 服务似乎一次被限制为 3 或 4 个并发客户端请求。如果我将应用程序池 Maximum Worker Processes 设置得更高(大约 10 个)或将服务行为 ConcurrencyMode 设置为 Multiple,我会获得更好的吞吐量(快几倍)。然而,这些似乎是解决实际问题的方法,带来了他们自己的问题。我错了,还是 IIS 应该能够在一个工作进程中启动我的 WCF 服务的许多实例(数十个或更多)来处理负载?我只知道我在某处缺少一个设置,但我找不到它。

编辑:到目前为止,在尝试这些建议时,我意识到我的数学计算与吞吐量有关。使用 ForEach 循环,我可以在 20 秒的时间内在服务器上获得估计的并发处理(每个任务持续时间 * 任务数 / 总运行时间)。对于正在完成的实际工作(睡眠 10 秒),这似乎仍然很低,但不再低得离谱。

第二次编辑:我将@Pablo 的评论标记为答案,因为他的答案加上他的链接为我提供了显着提高性能的信息(我认为是 3 倍)。但是,我想提出后续问题-在 WCF / IIS 中处理并发请求的合理期望是什么?假设 CPU、内存和 IO 不是瓶颈,处理请求的实际限制/期望(每个 CPU)是多少?我正在寻找的是一个经验法则,它告诉我,如果不添加 CPU(或工作进程),我可能不会获得任何更大的提升。再次感谢。

(在 Windows 2008 Server 上,由 IIS 托管,1 个处理器)
WCF 服务配置(略):

<?xml version="1.0"?>
<configuration>
  <configSections>
  <system.serviceModel>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
    <services>
      <service name="FMHIRWCFSvc.IRService" behaviorConfiguration="IRServiceBehavior">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="Binding1" contract="FMHIRWCFSvc.IIRService" />
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding name="Binding1" maxReceivedMessageSize="104857600">
          <readerQuotas maxArrayLength="104857600"/>
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="IRServiceBehavior">
          <serviceMetadata httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceThrottling
              maxConcurrentCalls = "500"
              maxConcurrentSessions = "500"
              maxConcurrentInstances = "500"
            />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
  <applicationSettings>
    <FMHIRWCFSvc.Properties.Settings>
      <setting name="FMHIRWCFSvc_ir_dev_websvc_IRWebService40" serializeAs="String">
        <value>http://ir-dev-websvc/imageright.webservice/IRWebService40.asmx</value>
      </setting>
    </FMHIRWCFSvc.Properties.Settings>
  </applicationSettings>
  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="500"/>
    </connectionManagement>
  </system.net>
</configuration>

客户端配置(缩写):

<?xml version="1.0"?>
<configuration>
  <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IIRService" closeTimeout="00:01:00"
          openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
          allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
          maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
          messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
          useDefaultWebProxy="true">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="https://{myserveraddress}/FMHIRWCFSvc/IRService.svc"
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IIRService"
        contract="wcf_local.IIRService" name="BasicHttpBinding_IIRService" />
    </client>
  </system.serviceModel>
  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="500"/>
    </connectionManagement>
  </system.net>
</configuration>

客户端方法调用:

static void WCFTesting()
{
    ConcurrentQueue<Exception> exceptionList = new ConcurrentQueue<Exception>();
    int[] taskList = new int[250];
    Parallel.ForEach(taskList, theTask => 
    {
        try
        {
            // Create the WCF client
            BasicHttpBinding binding = new BasicHttpBinding {
                Security = { Mode = BasicHttpSecurityMode.Transport },
                SendTimeout = TimeSpan.FromSeconds(20)
            };
            EndpointAddress endpointAddress = new EndpointAddress("https://{myserveraddress}/FMHIRWCFSvc/IRService.svc");
            IRServiceClient wcfClient = new IRServiceClient(binding, endpointAddress);

            // Call wcf service method that sleeps 10 seconds and returns
            wcfClient.TestCall();
        }
        catch (Exception exception) {
            // Store off exceptions for later processing
            exceptionList.Enqueue(exception);
        }
    });
}

WCF 服务代码:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
...
public string TestCall()
{
    Thread.Sleep(10000);
    return null;
}

感谢您的任何见解或建议!

【问题讨论】:

    标签: wcf iis throughput throttling


    【解决方案1】:

    您使用的测试机制可能并不完全正确。

    使用Parallel.For() 并不意味着它会并行创建 250 个工作器。在这种情况下,您似乎将基准测试限制在最适合您的客户端处理器配置的范围内,而不是实际测试服务器可以处理的内容。

    如果您真的想调用 250 个并行线程并查看它的反应,您可以手动创建所有线程。如:

            var exceptionList = new ConcurrentQueue<Exception>();
            const int max = 250;
            int numberOfTasks = max;
            var signal = new ManualResetEvent(false);
            for (var i = 0; i < max; i++)
            {
                var thread = new Thread(() =>
                {
                    try
                    {
                        // Create the WCF client
                        BasicHttpBinding binding = new BasicHttpBinding
                        {
                            Security = { Mode = BasicHttpSecurityMode.Transport },
                            SendTimeout = TimeSpan.FromSeconds(20)
                        };
                        EndpointAddress endpointAddress = new EndpointAddress("https://{myserveraddress}/FMHIRWCFSvc/IRService.svc");
                        IRServiceClient wcfClient = new IRServiceClient(binding, endpointAddress);
    
                        // Call wcf service method that sleeps 10 seconds and returns
                        wcfClient.TestCall();
                    }
                    catch (Exception exception)
                    {
                        // Store off exceptions for later processing
                        exceptionList.Enqueue(exception);
                    }
    
                    if (Interlocked.Decrement(ref numberOfTasks) == 0) signal.Set();
                });
                thread.Start();
            }
            signal.WaitOne();
    

    【讨论】:

    • 感谢您的帮助。当我使用您的手动线程创建代码时,我确实得到了并发性的提升(估计低 30 秒而不是 20 秒),但不是我希望的跳跃,比如允许许多 IIS 工作进程(网络花园)。我的想法是,如果增加工作进程的数量会大大提高性能,那么客户端不是瓶颈。如果服务器硬件没有征税(低 cpu 使用率、低内存使用率、低 I/O 活动)还有什么可能限制吞吐量?我真的是在最大化 WCF 服务,我的下一步是更多的工作进程吗?
    • 我认为一个问题可能与 WCF 和 IIS 如何处理 ThreadPool 有关。长时间运行的请求(尤其是在突发期间)存在一个固有问题,因为池中的线程数量非常稀少。有一篇非常透彻的帖子here 分析了 WCF 的线程行为、一些建议以及来自 Microsoft 的其他建议:support.microsoft.com/kb/2538826blogs.microsoft.co.il/blogs/idof/archive/2011/05/05/…
    • 感谢您的链接。我还没有阅读(并理解)您的所有链接,但我能够通过 IOCP 线程创建解决方法链接将性能提高一倍。这使我的测试方法平均并发处理了 78 个请求。我的真实世界场景同时存在 35 个左右。我想我已经用尽了所有实用的编程解决方案,需要更多地关注服务器管理解决方案。
    【解决方案2】:

    Parallel.ForEachTask.Start 以大致相同的代码结束。不能保证任务同时运行。

    最好使用对 WCF 服务 link 的异步调用进行测试。

    【讨论】:

    • 感谢您的回答。你是对的,异步会让客户端处理的不仅仅是 Parallel.ForEach。在寻找并行处理的快速方法时,我编写了一些测试客户端应用程序,发现手动创建线程给了我最大的吞吐量,其次是异步调用,其次是 Parallel.Foreach。但是,我认为我当前的瓶颈在服务器上,因为添加工作进程显着增加了我使用 Parallel.ForEach 的单个测试客户端应用程序的吞吐量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多