【发布时间】:2015-01-07 05:43:49
【问题描述】:
总结
托管在 IIS 中的 WCF 服务只有 1 个方法,需要 1 秒才能完成(示例代码中的 Thread.Sleep)。当 5 个“客户端”用请求敲击服务器时,平均响应时间为 1 秒,10 个客户端 - 大约 2 秒,而 20 个客户端的性能下降到最低水平。我尝试了所有类型的设置,但没有任何帮助。
我认为问题出在 WCF 中(MS 试图使其具有故障安全性,并为开发人员提供了很多保护措施):它试图使用尽可能少的线程,因此性能受到影响。
详细说明
代码
我在 IIS 中托管了一个非常简单的 WCF 服务:
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Threading;
namespace WCFPerf
{
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "Test")]
Stream DoWork(Stream s);
}
public class Service1 : IService1
{
public Stream DoWork(Stream s)
{
Thread.Sleep(1000); // simulate work
return s;
}
}
}
配置文件:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehavior" name="WCFPerf.Service1">
<endpoint address="" behaviorConfiguration="web" binding="customBinding" bindingConfiguration="" contract="WCFPerf.IService1" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:57676/Service1.svc" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<customBinding>
<binding closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00">
<webMessageEncoding />
<httpTransport manualAddressing="true" />
</binding>
</customBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="200" maxConcurrentSessions="200" maxConcurrentInstances="200" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.net>
<connectionManagement>
<add address="*" maxconnection="2000" />
</connectionManagement>
</system.net>
</configuration>
测试服:
void Main()
{
var numberOfThreads = new[] {5, 10, 20, 30};
var table = new List<ResultInfo>();
foreach (var threadNumber in numberOfThreads)
{
var tasks = new List<Task<List<double>>>();
for (var i = 0; i < threadNumber; i++)
{
tasks.Add(Task<List<double>>.Factory.StartNew(() =>
{
var results = new List<double>();
for (var j = 0; j < 5; j++)
{
results.Add(SendRequest());
}
return results;
}));
}
Task.WaitAll(tasks.ToArray());
var allResults = tasks.SelectMany(t => t.Result);
table.Add(new ResultInfo{ Threads = threadNumber, Avg = allResults.Average().ToString("F2"), Min = allResults.Min().ToString("F2"), Max = allResults.Max().ToString("F2") });
}
table.Dump();
}
public double SendRequest()
{
var stopwatch = new Stopwatch();
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(@"http://localhost:8081/");
stopwatch.Start();
var t = client.PostAsync("WCFPerf/Service1.svc/Test", new StringContent("123")).Result;
stopwatch.Stop();
return stopwatch.Elapsed.TotalSeconds;
}
}
public class ResultInfo
{
public int Threads {get;set;}
public string Avg {get;set;}
public string Min {get;set;}
public string Max {get;set;}
}
测试结果很不可预测,但总有一个不好的数字:
这次运行我将执行时间从 1 秒更改为 5 秒,只是为了证明差异(2 倍、3 倍等)是相对的,而不是绝对的。另请注意,“热身”会有所帮助,但只是一点点。
评论
在我所有的测试期间(我做了大约 100 次不同类型的运行),Windows 任务管理器在 w3wp 进程中报告的最大线程数为 54。我使用的是具有 2 个物理内核和 4 个逻辑内核的 Core i3 CPU,12 Gb RAM,在 Windows 8.1 和 .Net 4.5 下运行。进程的内存打印总是增长,但非常缓慢,最高值在 110 Mb 左右。
dotTrace 报告说,大约 7% 的时间花在我的代码和其他所有内容上 - 在系统代码中。
我已尝试执行以下操作:
在C:\Windows\Microsoft.NET\Framework\v4.0.30319\Aspnet.config:
<system.web>
<applicationPool maxConcurrentRequestsPerCPU="5000" maxConcurrentThreadsPerCPU="0" requestQueueLimit="5000"/>
</system.web>
在C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config
<system.net>
<connectionManagement>
<add address = "*" maxconnection = "400" />
</connectionManagement>
</system.net>
<system.web>
<processModel autoConfig="false" maxWorkerThreads="80" maxIoThreads="80" />
<httpRuntime minFreeThreads="10" minLocalRequestFreeThreads="10" />
链接:Web Settings Schema、 Element、applicationPool maxConcurrentRequestsPerCPU、 Element、Thread Throttling in IIS-hosted WCF、processModel Element、httpRuntime Element、ASP Registry Entries (IIS 6.0)
想法
我的理由(它应该如何工作):虽然我们有足够的内存供每个 IIS/WCF 使用,但会为每个请求创建一个新线程。例如,当我们有 40 个“客户端”连接到服务器时 - w3wp 进程中将有大约 45 个线程(40 个工作线程和一些其他线程用于很好的衡量)。因为我所有的“客户”在发送下一个请求之前都在等待响应,所以响应时间可能会有一点差异(比如 20-30%,而不是我现在看到的 2 倍甚至 10 倍)。
问题(如我所见):
- 线程不足(70 个客户端只有 54 个线程)
- 存在某种队列,因为已经有 20 个“客户”,平均响应时间至少比实际逻辑长两倍
- 存在不确定的行为,因为每次运行的结果差异很大
【问题讨论】:
-
请务必注意,客户端 Windows 版本(如 Windows 7 或 Windows 8.1)存在限制。这样做是为了防止将客户端系统用于服务器的东西。因此,要获得任何合理的负载测试性能,您需要添加服务器(如 Windows Server 2008 R2 或 Windows Server 2012)
标签: performance wcf iis threadpool scalability