我已经创建了示例.NET Core WebApi应用程序,以测试异步方法如何增加吞吐量。应用托管在IIS 10上。
这是我的控制器的代码:
[HttpGet("sync")]
public IEnumerable<string> Get()
{
return this.GetValues().Result;
}
[HttpGet("async")]
public async Task<IEnumerable<string>> GetAsync()
{
return await this.GetValues();
}
[HttpGet("isresponding")]
public Task<bool> IsResponding()
{
return Task.FromResult(true);
}
private async Task<IEnumerable<string>> GetValues()
{
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
return new string[] { "value1", "value2" };
}
有一些方法:
Get()
-同步获取结果
GetAsync()
-异步获取结果。
IsResponding()
-检查服务器是否可以处理请求
然后,我创建了示例控制台应用程序,该应用程序创建了100个控制器的同步请求和异步方法请求(不等待结果)。然后,我调用方法IsResponding()
来检查服务器是否可用。
控制台应用程序代码为:
using (var httpClient = new HttpClient())
{
var methodUrl = $"http://localhost:50001/api/values/{apiMethod}";
Console.WriteLine($"Executing {methodUrl}");
//var result1 = httpClient.GetAsync($"http://localhost:50001/api/values/{apiMethod}").Result.Content.ReadAsStringAsync().Result;
Parallel.For(0, 100, ((i, state) =>
{
httpClient.GetAsync(methodUrl);
}));
var sw = Stopwatch.StartNew();
var isAlive = httpClient.GetAsync($"http://localhost:50001/api/values/isresponding").Result.Content;
Console.WriteLine($"{sw.Elapsed.TotalSeconds} sec.");
Console.ReadKey();
}
其中{apiMethod}
是“同步”还是“异步”,具体取决于用户输入。
在两种情况下,服务器长时间没有响应(大约40秒)。 我表示,在异步情况下,服务器应继续快速处理请求,但事实并非如此。
更新1: 我已经这样更改了客户端代码:
Parallel.For(0, 10000, ((i, state) =>
{
var httpClient = new HttpClient();
httpClient.GetAsync($"http://localhost:50001/api/values/{apiMethod}");
}));
using (var httpClient = new HttpClient())
{
var sw = Stopwatch.StartNew();
// this method should evaluate fast when we called async version and should evaluate slowly when we called sync method (due to busy threads ThreadPool)
var isAlive = httpClient.GetAsync($"http://localhost:50001/api/values/isresponding").Result.Content;
Console.WriteLine($"{sw.Elapsed.TotalSeconds} sec.");
}
并调用IsResponding()
方法执行了很长时间。
更新2 是的,我知道异步方法如何工作。是的,我知道如何使用HttpClient。这只是证明理论的一个例子。
更新3 正如 StuartLC 在其中一项注释中提到的那样,IIS以某种方式限制或阻止了请求。当我以SelfHosted身份启动WebApi时,它开始按预期方式工作:
答案 0 :(得分:1)
您似乎不了解异步。它不会使响应返回更快。直到动作完成的所有内容(异步或不异步)之前,都无法返回响应。如果有的话,异步实际上要更慢,甚至稍微慢一点,因为异步处理涉及额外的开销,而同步处理则不必要。
异步所做的可能会允许为请求提供服务的活动线程返回到池中,以服务其他请求。换句话说,异步是关于规模而不是性能。仅当服务器被请求猛击时,您才会看到好处。然后,当传入的请求通常已排队同步时,您将处理一些异步任务中的其他请求,从而使它们的线程丧失原因。此外,不能保证完全释放该线程。如果异步任务立即完成或立即完成,则线程将被保留,就像同步一样。
编辑
您还应该认识到IIS Express是单线程的。因此,这不是性能调整的好方法。如果您同时运行1000个请求,则会立即排队999个。然后,您无需执行任何异步工作-仅返回已完成的任务。这样,线程将永远不会被释放,因此在这种情况下,同步和异步之间实际上没有区别。因此,您可以确定处理999个请求队列所需的时间(最后还要进行状态检查)。如果您执行以下操作,则可能会更好地消除差异:
await Task.Delay(500);
代替返回Task.FromResult
。这样,线程上就会有实际的空闲时间,这可能会使它返回到池中。
答案 1 :(得分:0)
在向ASYNC方法发出一堆请求后,isresponsible
方法的执行时间非常快,大约为0.02秒。
一束向SYNC方法的请求后,isresponsible
方法的执行时间非常慢,大约需要35秒。
答案 2 :(得分:-1)
我不确定这是否会带来任何重大改进,但是您应该在服务器中每个等待的位置(包括ConfigureAwait(false)
中调用GetAsync
。
它应该更好地利用线程。