如何在收到请求时使用异步代码加速 API 控制器响应以运行另一个任务?



在我的 api 控制器中,我有一个名为"SendMail"的方法,它接收一个带有 json 正文的帖子,如下所示:

{
"MailTo": "abcdefg@hijklmn.opqrst",
"Subject": "This is the subject",
"Body": "This is the body"
}

收到请求后,"SendMail"方法会验证数据,如果数据正确,则发送邮件。

[HttpPost]
public IHttpActionResult SendMail([FromBody] MailData mailData)
{
if(ValidateMailData(mailData) == true)
{
Email.send(mailData);
return Ok("Your email was sent!");
}
return BadRequest("Some error");
}

问题是调用方只会在发送邮件时收到来自 api 的响应(并且需要很长时间),但我只想验证数据,将响应返回给调用方,然后异步发送邮件(邮件是否实际发送对调用方来说并不重要, 如果数据验证正确,它将被发送)。这段代码只是一个示例,但准确地说明了我面临的问题。

您应该将应用程序的 Web 层与实际的服务层分离。
这通常是通过使用队列来完成的。

这个想法是,您有一个 Web 图层,其职责是获取用户对要完成的某种工作的请求,在您的情况下,实际工作是发送电子邮件。实现应用的 Web 层的 Web 服务应公开一个终结点,用户可以调用该终结点以请求发送电子邮件。Web 服务终结点应根据依赖于你的要求的业务规则验证用户请求的内容。

如果用户请求是有效的请求,则将请求排入专用队列,然后返回 202 接受状态代码响应(请注意,在回复 HTTP 请求之前不必实际发送电子邮件,只需确保用户请求已成功排队)。

否则,如果用户请求根据验证规则无效,您将返回 422 无法处理的实体状态代码,以便用户了解其请求无效。

同时,排队的请求由一个或多个工作器服务处理,这些工作器服务是独立的应用程序(它们与 Web 服务器完全分离),其职责是实现发送电子邮件的实际工作。

您可以决定对 Web 服务器和后端服务使用不同的技术,它们只需要就 Web 服务器排队和后端服务取消排队的消息的格式达成一致。有很多方法可以做到这一点。

请注意,这种体系结构比仅使用 Web 服务要复杂一些,但它具有许多优点。关注点有明确的分离,基础设施的每个部分都能够很好地扩展,因为它只做一件事,并且系统的不同部分没有任何共享资源争用(嗯,队列实际上是有争议的,但有非常好的技术可以帮助您以正确的方式实现队列, 例如服务总线)。

另一个需要注意的重要事项是,在 Web 服务层中使用异步代码不是更快地回复单个用户的方法,而是一种有助于更好地扩展 Web 服务器本身的技术。线程是重要的资源,Web 服务器使用它来回复传入的 HTTP 请求。执行 IO 任务时,切勿阻止线程,让它空闲等待 IO 任务完成。相反,您应该使用异步 api,以便您的线程启动异步任务(例如,排队发送电子邮件的新请求),并将其立即返回到线程池,以便它可以用于回复另一个传入的 HTTP 请求(或执行其他有用的操作),同时操作系统正在执行 IO 操作。

想想这个类比(这最初是由Jon Skeet写的)。想象一下,你在家里,你想吃披萨。你决定打电话给你最喜欢的餐厅并订购披萨(在这个类比中,你是线程,在家里买披萨是IO任务)。

你认为在披萨从餐厅到你家的时候订购披萨然后做其他有用的事情,还是坐在门前等待披萨而在此期间什么都不做更有效?好吧,如果你是一个网络服务器,你很忙,而且当披萨运到你家时,你肯定有很多重要的事情要做(阅读服务传入的HTTP请求)。

最新更新