我通常在我的 Web 应用程序中使用许多 EF Core
异步方法,就像这样:
await db.Parents.FirstOrDefaultAsync(p => p.Id == id);
我们知道,ThreadPool
中默认的线程数目是受限于 CPU 逻辑内核数的。同时,用户请求也会被 ThreadPool
中的线程处理。
对于我的应用程序中的许多异步调用,我是否需要担心处理用户请求或性能问题?
我通常在我的 Web 应用程序中使用许多 EF Core
异步方法,就像这样:
await db.Parents.FirstOrDefaultAsync(p => p.Id == id);
我们知道,ThreadPool
中默认的线程数目是受限于 CPU 逻辑内核数的。同时,用户请求也会被 ThreadPool
中的线程处理。
对于我的应用程序中的许多异步调用,我是否需要担心处理用户请求或性能问题?
我需要担心应用程序中的许多异步调用导致的用户请求或性能问题吗?
EF Core为存储库提供异步查询接口。是否全程异步,或者某些方法是否会阻塞线程池线程取决于EF提供程序。SQLServer的SqlClient具有不阻塞线程的基于任务的异步方法。大多数其他提供程序也是如此。但例如对于EF内存提供程序,或者可能是SQLite提供程序,它可能是异步/同步,要么同步完成并返回已完成的任务,要么阻塞线程池线程。
因此,EF通常不会阻止您的线程。当您向数据库发出异步调用时,它会释放应用程序的线程以执行更多工作,例如处理其他请求。如果您向数据库发出太多并发请求,则每个请求开始花费更多时间。
当这种情况发生时,您需要有一种机制来减缓向数据库发送新请求的速率,否则您将陷入糟糕的状态。例如,数据库服务器有2000个正在运行的请求,其中大部分代表已经放弃并超时的客户端。由于所有旧请求,新请求无法及时处理。
通常情况下,当您增加并发时,吞吐量会增加到某个点,但超过该点后,总体吞吐量会下降,有时会急剧下降。大致如下:你需要限制整体并发以防止吞吐量严重降低。最好及早失败一些请求(例如使用HTTP 503),而不是接受所有请求,但在SLA内没有完成。
使用同步数据库访问的好处之一是它会占用应用程序线程的时间来进行数据库交互,自动向请求流添加反压力。当所有线程池线程都很忙时,让请求等待线程池线程实际上是一件好事。当你完全异步化时,这种控制就消失了,你需要考虑如何替换它。
ASP.NET Core目前没有内置的限流功能。你的Web服务器主机可能有一些,例如SqlConnection的连接池限制可用于限制每个应用程序实例的并发请求数量。但你必须有某种东西,可以使你有条理地处理请求量激增。
ThreadPool
的同步使用(即阻塞 I/O)并不比异步使用(即异步 I/O)更好。前者隐含地对较低层施加某种限制并不是比显式地施加合理的限制更有益,也许可以扩展后续层次。如果处理能力、内存或带宽超过其容量,那么大型同步 Web 农场和小型异步 Web 农场对数据库有什么区别呢?然而,在 Web 应用程序层面存在很大的基础设施差异。 - acelentasync
即使在单个数据库情况下。尽管我理解他们的担忧,但这似乎是几乎有效的。 - Arman Ebrahimpour
DbContext
上的异步方法不会导致使用额外的线程,无论是在线程池中还是其他地方。 - Bradley Uffner