我们最近基于SOA开发了一个网站,但是在负载下出现了严重的负载和性能问题。我在这里发布了一个与此问题相关的问题:
该网站由一个API(WEB API)站点和一个Web站点组成,它们分别托管在4节点群集上,并调用API。两者都使用ASP.NET MVC 5进行开发,所有操作/方法都基于async-await方法。
在运行网站时,我们使用了一些监控工具(如NewRelic),调查了几个转储文件并对工作进程进行了分析后,发现在非常轻的负载(例如16个并发用户)下,我们最终拥有了约900个线程,利用了100%的CPU,并填满了IIS线程队列!
即使我们通过引入大量缓存和性能改进来将网站部署到生产环境中,我们团队中的许多开发人员仍然认为我们必须删除所有异步方法,并将API和Web站点转换为普通的Web API和Action方法,它们只是简单地返回一个Action结果。
我个人不满意这种方法,因为我的直觉是我们没有正确使用异步方法,否则这意味着微软引入了一个基本上是相当破坏性和无法使用的功能!
您是否了解任何可以澄清异步方法应该/可以在哪里以及如何使用它们以避免此类情况的参考资料?例如,根据我在MSDN上阅读的内容,我认为API层应该是异步的,但Web站点可以是普通的非异步ASP.NET MVC站点。
更新:
以下是处理与API的所有通信的异步方法。
public static async Task<T> GetApiResponse<T>(object parameters, string action, CancellationToken ctk)
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(BaseApiAddress);
var formatter = new JsonMediaTypeFormatter();
return
await
httpClient.PostAsJsonAsync(action, parameters, ctk)
.ContinueWith(x => x.Result.Content.ReadAsAsync<T>(new[] { formatter }).Result, ctk);
}
}
这种方法有什么可笑的地方吗?请注意,当我们将所有方法转换为非异步方法时,性能得到了大幅提升。
以下是一个示例用法(我删去了与验证、日志记录等相关的其他代码。此代码是MVC动作方法的主体)。
在我们的服务包装器中:
public async static Task<IList<DownloadType>> GetSupportedContentTypes()
{
string userAgent = Request.UserAgent;
var parameters = new { Util.AppKey, Util.StoreId, QueryParameters = new { UserAgent = userAgent } };
var taskResponse = await Util.GetApiResponse<ApiResponse<SearchResponse<ProductItem>>>(
parameters,
"api/Content/ContentTypeSummary",
default(CancellationToken));
return task.Data.Groups.Select(x => x.DownloadType()).ToList();
}
在行动中:
public async Task<ActionResult> DownloadTypes()
{
IList<DownloadType> supportedTypes = await ContentService.GetSupportedContentTypes();
Task.Run
包装同步 API,而不是使用本来就是异步的 API?https://dev59.com/tXzaa4cB1Zd3GeqPOlft。通常,在服务器上使用Task.Run
是一个坏主意。 - noseratio - open to work