gpt4 book ai didi

performance - 异步和等待 : are they bad?

转载 作者:行者123 更新时间:2023-12-03 09:41:37 34 4
gpt4 key购买 nike

我们最近开发了一个基于 SOA 的站点,但是这个站点在负载过重时最终会出现严重的负载和性能问题。我在这里发布了一个与此问题相关的问题:

ASP.NET website becomes unresponsive under load

该站点由托管在 4 节点集群上的 API(WEB API)站点和托管在另一个 4 节点集群上并调用 API 的网站组成。两者都是使用 ASP.NET MVC 5 开发的,所有操作/方法都基于 async-await 方法。

在 NewRelic 等监控工具下运行该站点,调查几个转储文件并分析工作进程后,事实证明,在非常轻的负载(例如 16 个并发用户)下,我们最终拥有大约 900 个线程,这些线程占用了 100% 的 CPU并填满了 IIS 线程队列!

尽管我们通过引入大量缓存和性能修正来设法将站点部署到生产环境,但我们团队中的许多开发人员认为我们必须删除所有异步方法并将 API 和网站转换为普通的 Web API 和操作方法只需返回一个 Action 结果。

我个人对这种方法不满意,因为我的直觉是我们没有正确使用异步方法,否则这意味着微软引入了一个基本上具有破坏性且无法使用的功能!

您是否知道任何引用资料可以清楚地说明应该/可以在何处以及如何使用异步方法?我们应该如何使用它们来避免这样的戏剧?例如根据我在 MSDN 上阅读的内容,我认为 API 层应该是异步的,但该网站可能是一个普通的非异步 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();

最佳答案

Is there anything silly with this method? Note that when we converted all method to non-async methods we got a heaps better performance.



我可以看到至少有两件事在这里出错:
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);
}
}

首先,您传递给 ContinueWith 的 lambda 是阻塞的:
x => x.Result.Content.ReadAsAsync<T>(new[] { formatter }).Result

这相当于:
x => { 
var task = x.Result.Content.ReadAsAsync<T>(new[] { formatter });
task.Wait();
return task.Result;
};

因此,您正在阻塞恰好执行 lambda 的池线程。 这有效地扼杀了自然异步 ReadAsAsync API 的优势,并降低了 Web 应用程序的可伸缩性。在您的代码中注意其他类似的地方。

其次,ASP.NET 请求由安装了特殊同步上下文 AspNetSynchronizationContext 的服务器线程处理。当您使用 await 进行延续时,延续回调将被发布到相同的同步上下文,编译器生成的代码将处理这一点。 OTOH,当您使用 ContinueWith 时,这不会自动发生。

因此,您需要显式提供正确的任务调度程序,移除阻塞的 .Result(这将返回一个任务)并 Unwrap 嵌套任务:
return
await
httpClient.PostAsJsonAsync(action, parameters, ctk).ContinueWith(
x => x.Result.Content.ReadAsAsync<T>(new[] { formatter }),
ctk,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()).Unwrap();

也就是说,你真的不需要在这里增加 ContinueWith 的复杂性:
var x = await httpClient.PostAsJsonAsync(action, parameters, ctk);
return await x.Content.ReadAsAsync<T>(new[] { formatter });

Stephen Toub 的以下文章高度相关:

"Async Performance: Understanding the Costs of Async and Await"

If I have to call an async method in a sync context, where using await is not possible, what is the best way of doing it?



你几乎不需要混合 awaitContinueWith ,你应该坚持使用 await 。基本上,如果你使用 async ,它必须是 async "all the way"

对于服务器端 ASP.NET MVC/Web API 执行环境,它只是意味着 Controller 方法应该是 async 并返回一个 TaskTask<> ,检查 this 。 ASP.NET 跟踪给定 HTTP 请求的挂起任务。在完成所有任务之前,该请求不会完成。

如果您确实需要从 ASP.NET 中的同步方法调用 async 方法,可以使用 AsyncManager 类似 this 来注册挂起的任务。对于经典的 ASP.NET,您可以使用 PageAsyncTask

在最坏的情况下,您会调用 task.Wait() 并阻止,因为否则您的任务可能会在该特定 HTTP 请求的边界之外继续进行。

对于客户端 UI 应用程序,从同步方法调用 async 方法可能会有一些不同的场景。例如,您可以使用 ContinueWith(action, TaskScheduler.FromCurrentSynchronizationContext()) 并从 action 触发完成事件(如 this )。

关于performance - 异步和等待 : are they bad?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21771219/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com