我们有一个典型的企业API,作为面向客户的SPA的后端.许多API请求涉及调用许多下游Web服务,我们现在通常使用TAP(async / await)将其转换为异步I / O.许多远程服务调用是并行启动的,无需等待,然后使用Task.WhenAll批量等待.有时WhenAll是在一定数量的任务上完成的,有时我们会在集合中为每个项目启动一个远程调用 – 这会导致产生一个未知(但通常是低,十几个)的任务,然后用WhenAll等待它们.
As I understand,这导致在ThreadPool上安排这些任务的延续(即反序列化其响应的逻辑).这引出了我的问题:并行运行这些cpu限制的延续不会导致ThreadPool上的压力过大?我们是否应该开发某种中间件,通过将它们安排到自定义调度程序来限制在单个请求中启动的异步I / O任务的延续的并发性?
通过减少任何给定请求分配的ThreadPool线程的数量,这将允许我们的应用程序更好的可伸缩性,这将允许在我们开始耗尽ThreadPool线程之前提供更多的并发请求(或者被ThreadPool限制增长)?
或者这是相当无用的,我们应该简单地信任默认的ThreadScheduler ThreadPool能够在任意数量的并发请求中将所有任务安排到可用的cpu内核?
每个人都试图回答:
这是一个非常大的公司的一个相当成熟的系统,由几十位专家(包括我自己)仔细审查,在一个美国州的生产中,即将进入国家层面的生产.像“先测量”,“知道你是否与cpu绑定”,“尝试AppInsights”和“不要试图比微软更聪明”这样的建议是我们自己明显想到的第一件事.我们正在寻求的指导级别更像是:有没有人在美国国家级实现了一个全异步的ASP.NET Web API系统,并且拥有async / WhenAll并发的真实体验?
解决方法
但我会提到坚实的建议,永远不要优先考虑.今天的中端桌面可以轻松处理16的线程池(通常可以处理数百个等待任务),一个好的服务器可以处理比这更大的线程池,你的时间成本与替换成本相比多少或者新服务器,可能不到6个月,很可能不到3个月.
任何性能问题都归结为瓶颈,系统只能执行最慢的瓶颈.因此,在现实世界系统中,改善这些特定瓶颈的方法是如何提高性能.如何扩展这些瓶颈是如何提高可扩展性的.桌面和服务器硬件之间仍然存在很多差异,因此我再次强调,在花费数周/数月的时间来解决“问题”之前,您需要查看真实世界的非体外指标.可能不在那里.
如上所述,GraphQL允许您重新安排瓶颈发生的位置,允许您更均匀地分配它们,以更好地利用您已有的硬件.许多更现代的设计模式也是如此.
总之,限制一个过程,只要它成为一个问题,这是有意义的.