执行一些需要 5 秒的事情(如电子邮件发送)但立即返回并回复?

Posted

技术标签:

【中文标题】执行一些需要 5 秒的事情(如电子邮件发送)但立即返回并回复?【英文标题】:Execute something which takes 5 seconds (like email send) but return with response immediately? 【发布时间】:2021-12-14 16:29:14 【问题描述】:

上下文

在 ASP.NET Core 应用程序中,我想执行一个需要 5 秒的操作(例如发送电子邮件)。我确实知道 async/await 及其在 ASP.NET Core 中的用途,但是我不想等待操作结束,而是想立即返回给客户端。

问题

所以无论是自制软件,还是 Hangfire 的 BackgroundJob.Enqueue<IEmailSender>(x => x.Send("hangfire@example.com"));,这有点像“一劳永逸”

假设我有一些更复杂的方法,其中注入了 ILogger 和其他东西,我想触发并忘记该方法。该方法中有错误处理和日志记录。(注意:对于 Hangfire 来说不是必需的,这个问题与后台工作人员的实现方式无关)。我的问题是该方法将完全脱离上下文运行,内部可能没有任何作用,没有 HttpContext(我的意思是 HttpContextAccessor 会给出 null 等)所以没有用户,没有会话等。

问题

如何正确解决这个特定的电子邮件发送问题?没有人愿意等待 5 秒的响应,同时没有人愿意抛出和发送电子邮件,如果发送操作返回错误,甚至不记录......

【问题讨论】:

那么,为什么不直接使用 Hangfire 呢? ...请重新阅读粗体字,然后是问题... OK.... Hangfire 不需要 HttpContext 是我,需要httpcontext,例如知道用户是谁等。同样使用瞬态和作用域实例,当脱离上下文使用时,这些都变成废话 然后将该数据包装成一个对象并将其传递给 Hangfire。没有其他安全方法,因为当 HTTP 请求完成时上下文将被释放。 【参考方案1】:

如何正确解决这个特定的电子邮件发送问题?

这是“从我的网络应用程序运行后台作业”问题的特定实例。

没有通用的解决方案

有——或者至少有一个通用的模式;只是许多开发人员试图避免它,因为它并不容易。

我在basic distributed architecture 上的博客文章系列中对它进行了非常完整的描述。我认为要承认的一件重要事情是,由于您的后台工作(发送电子邮件)是在 HTTP 请求之外完成的,因此它确实应该在您的 Web 应用程序进程之外完成。一旦你接受了这一点,剩下的解决方案就到位了:

    您需要一个用于工作的持久存储队列。 Hangfire 使用您的数据库;我更喜欢 Azure 存储队列等云队列。 这意味着您需要复制所有需要的数据,因为它需要序列化到该队列中。同样的限制也适用于 Hangfire,只是并不明显,因为 Hangfire 运行在同一个 Web 应用程序进程中。 您需要一个后台进程来执行您的工作队列。我更喜欢 Azure Functions,但另一种常见的方法是将 ASP.NET Core Worker Service 作为 Win32 服务或 Linux 守护程序运行。 Hangfire 有自己的临时进程内线程。在进程内运行 ASP.NET Core 托管服务也可以,尽管这与 Hangfire 有一些相同的缺点,因为它也在 Web 应用程序进程中运行。 最后,您的工作队列处理器应用程序有自己的服务注入,如果需要,您可以将每个工作队列项编码为 create a dependency scope。

IMO,这是一个正常的阈值,随着您的 Web 应用程序“成长”而达到。它比简单的 Web 应用程序更复杂:现在您有了一个 Web 应用程序、一个持久队列和一个后台处理器。因此,您的部署变得更加复杂,您需要考虑诸如对工作队列模式进行版本控制这样的事情,这样您就可以在不停机的情况下进行升级(Hangfire 无法很好地处理)等等。一些开发人员对此非常犹豫,因为当“他们想要做的只是发送一封电子邮件而不等待它,但事实是,当一个婴儿网络应用程序被分发时,这是向上的必要步骤。

【讨论】:

以上是关于执行一些需要 5 秒的事情(如电子邮件发送)但立即返回并回复?的主要内容,如果未能解决你的问题,请参考以下文章

PCB SQL SERVER 发送邮件(异步改同步)

Swiftmailer不立即发送

消息队列或调度程序

一旦违反条件,停止存储过程的进一步执行?

在采集程序中增加定时发送邮件以及关机处理的实现

如果进程花费的时间超过设置的时间间隔但不退出,C# 会做一些事情