这个MVC4 API调用应该是异步的吗?

本文关键字:异步 MVC4 API 调用 这个 | 更新日期: 2023-09-27 18:09:48

我刚刚继承了一个MVC4 API应用程序,它作为一些其他服务的基本请求转接器。应用程序的基本结构是这样的:

[request] -> [rest api] ->[本地处理]->[对外部服务的同步调用]->[本地处理]-> [response]

本地处理主要是关于验证和将内容保存到db中。一点也不重。在极端情况下,外部请求可能需要1 - 200秒以上的时间。API通常每小时处理数千个请求。

应用程序托管在一个单一的,小的,azure云实例。

我感到困惑的是线程。从API处理程序方法到对外部服务的外部调用的整个过程都被设置为异步的:

public async Task<CustomResponseType> Post([FromBody] inputType) {
   // some validation
   ...
   // save some stuff to a db
   ...
   var response = await httpClient.PostAsync(someRequest)
   return await response.Content.ReadAsStringAsync();
}

首先,让这个过程异步化的优势是什么?根据我的理解,IIS应该为传入请求管理自己的线程池,并且由于我们正在等待对单个外部服务调用的同步响应,因此看起来没有任何事情实际上被并行处理。乍一看,似乎异步服务请求将竞争IIS将使用的相同资源,实际上,同步处理所有服务请求可能会更有效。

我读了这个:http://msdn.microsoft.com/en-us/library/ee728598%28v=vs.98%29.aspx?wa=wsignin1.0并理解在IIS方面可能存在所谓的"线程饥饿"。那篇文章支持异步在这种情况下可能会有所帮助的观点,但我很难理解其中的原因。增加IIS线程的数量会更容易吗?他们不是都在争夺同样的资源吗?是否存在IIS线程使用起来更重的问题?

这个MVC4 API调用应该是异步的吗?

使用async/await并不意味着"生成新的线程来进行这些异步调用"。

Eric Lippert在他严肃的c# 5异步编程中描述了使用async/await的过程:

方法上的"async"修饰符并不意味着"此方法是"自动调度在工作线程上异步运行" 。它意思是与之相反的;它表示 "此方法包含控件流包含等待异步操作,因此将被编译器重写为延续传递风格,以确保异步操作可以在右侧恢复此方法点" 异步方法的全部意义在于你保持在当前线程尽可能多。它们就像协程:异步的方法为c#带来了单线程协同多任务。

当你在一个方法上使用async修饰符时,编译器根据你的方法的流程和await关键字在其中的使用来推断它作为一个符号并生成一个状态机。它使用任何额外的ThreadPool线程,相反,当你在I/O绑定异步方法上await时,编译器将控制权交还给调用者,这在ASP的情况下。. NET将让当前线程返回到ASP. NET。. NET线程池用于其他进入的请求。一旦I/O工作完成,延续将通过同样由ThreadPool分配的IOCP线程调用,这意味着你实际上让线程做更多的事情,而不需要创建新的线程。

有很多很棒的帖子描述了这个效果:

  1. There Is No Thread - By Stephan Cleary
  2. 使用async和await来提高ASP的性能。网络应用
  3. ASP。. NET async/await第2部分
  4. 在ASP中使用异步方法的魔力. NET 4.5加上一个重要的gotcha (By Scott Hanselman)为什么使用异步请求而不是使用更大的线程池?

由于外部调用可能需要长达200秒,那么是的,这似乎是异步控制器的完全合适的使用。

当你有大量的非cpu绑定阻塞请求时,最好使用异步控制器——I/O,就像你现在拥有的,是一个完美的例子。标准的"同步"方法将在外部服务调用期间阻塞线程。假设您有10个IIS工作线程(我知道这是一个人为的低数字),并且几乎同时接收10个服务调用。由于某种原因(可能是由于传递的参数),每个服务调用需要100秒。由于每个线程都在等待来自外部服务的响应,您的线程池已经耗尽——IIS无法处理任何额外的客户端。

另一方面,使用异步调用,. net框架将请求"搁置"到外部服务,将其委托给系统进程。然后释放该线程以处理其他客户端。一旦请求返回,该特定线程上的执行流将返回给异步方法。如果您熟悉Javascript,您可以将await之后的所有内容想象为回调方法。

这对于简单地增加线程数非常有利,因为当执行流在await之后返回时,异步方法不需要昂贵的上下文切换。总而言之,async提供了一种更好的方式来处理并发I/o绑定操作(注意:在处理cpu密集型并发进程时,async失去了一些优势)。

本文可能有所帮助:http://blog.stevensanderson.com/2010/01/25/measuring-the-performance-of-asynchronous-controllers/