我们正在排查.NET Core API
端点上的以下性能问题:
- 在次要负载下,终结点始终以不到
500MS
的速度返回。 - 当我们从 3 个浏览器到达端点时,每秒一个请求,它会逐渐变慢(在添加第三个浏览器进行调用的一分钟内,响应时间会下降到
50,000MS
或更糟。 - 每个额外的浏览器都会添加 API 使用的线程,例如 40 个线程基础,第二个浏览器命中端点导致 52 个线程,第三个峰值达到 70 个,依此类推。
- 加载一个终结点时,整个 API 会缓慢返回(所有终结点(。这是我考虑"线程耗尽"的主要原因,以及第 #3 点。
代码当前如下所示:
public IActionResult GetPresentationByEvent(int eventid)
{
return Authorized(authDto =>
{
var eventList = _eventService.GetPresentationByEvent(eventid);
return Ok(eventList)
})
}
我的理论是,return Authorized(authDto =>
保持一根线,直到它返回,导致线耗尽。
public async Task<IActionResult> GetPresentationByEvent(int eventid)
{
return Authorized(async authDto =>
{
Task<List<whatever>> eventList = _eventService.GetPresentationByEvent(eventid);
return Ok(eventList)
}
}
Authorized
是第三方库的一部分,所以我不能轻易测试。想知道这是否看起来像一个可能的问题/解决方案。
是的,异步等待可以减少线程耗尽。简而言之,当您生成的任务多于 ThreadPool 可以处理的任务时,就会出现线程耗尽。
您可以在此处检查一些微妙的指定: 线程不足和排队
您唯一需要记住的是,您永远不应该在任务中阻塞。这意味着使用 async await 调用异步代码(并且永远不要使用 .等待或 。未完成任务的结果(。
如果您使用某些不使用异步等待模式的阻塞代码,则必须在专用线程(而不是任务线程队列(上生成它。
我的理论是返回授权(authDto => 保留一个线程,直到它返回,导致线程耗尽。
是的。您可以通过查看方法的返回值来轻松判断该方法是否同步。IActionResult
不是可等待的类型,因此此方法将同步运行。
授权是第三方库的一部分,所以我不能轻易测试。想知道这是否看起来像一个可能的问题/解决方案。
可能。这完全取决于Authorized
是否可以处理异步委托。如果可以,那么这样的事情就会起作用:
public async Task<IActionResult> GetPresentationByEvent(int eventid)
{
return Authorized(async authDto =>
{
Task<List<whatever>> eventList = _eventService.GetPresentationByEventAsync(eventid);
return Ok(await eventList);
});
}
注意:
- 任务在传递给
Ok
或其他帮助者之前应await
。 - 这引入了
GetPresentationByEventAsync
,假设您的数据访问代码可以异步。
由于使GetPresentationByEvent
异步可能需要一些工作,因此值得在尝试此操作之前调查Authorized
是否可以采用异步委托。
使用异步等待可以避免线程耗尽吗?
是和不是。异步代码(包括async
/await
(确实使用较少的线程,因为它避免了阻塞线程。但是,仍然有限制。线程耗尽仍然是可能的,因为异步代码需要一个空闲线程才能完成。使用异步代码,通常可以在遇到可伸缩性问题(如线程耗尽(之前实现一两个数量级的可伸缩性。
有关async
ASP.NET 的更多概念性信息,请参阅此 MSDN 文章。