用于后台任务的asp.net框架中的Ihostedservice等价物

Posted

技术标签:

【中文标题】用于后台任务的asp.net框架中的Ihostedservice等价物【英文标题】:Equivalent of Ihostedservice in asp.net framework for background tasks 【发布时间】:2018-12-25 16:20:56 【问题描述】:

我在 .net 4.6.2 中有一个安静的微服务(web api),我想在每次调用某些端点进行一些数据库清理工作后调用一个 fire and forget 函数。我不想使用 Task.Run,​​我希望有一个非常可靠的专用进程来完成这项工作。

我发现了很多关于如何在 .net core 中使用 IhostedService 来执行长时间运行的后台任务的文章和讨论。

    Implement background tasks in microservices with IHostedService and the BackgroundService class ASP.NET Core background processing with IHostedService Background tasks with hosted services in ASP.NET Core

但是,我在 .net 框架中没有找到太多关于等效项的信息。那是 IRegisteredObject 吗?有人可以帮助我或指导我找到正确的资源。

注意:我们将 API 作为 Windows 服务托管,不使用 IIS

这是我第一次在这里提问,如果问题不够清楚,敬请见谅。

【问题讨论】:

你找到解决这个问题的方法了吗? 并非如此。我们将一条记录插入数据库队列并使用 Windows 服务来处理记录并完成工作。 这是hangfire.io 的完美用例。这可以在 IIS 或 windows 服务中运行。 docs.hangfire.io/en/latest/background-processing/… 如果您使用的是微服务方法,那么按照微服务架构(我认为的方式)将其拆分为一个单独的进程。将消息发布到消息总线并拥有处理消息的服务。将它添加到现有的 API 感觉少了很多微服务,而更像是 IMO。 在我们的应用程序中,我们有很多后台任务。当用户执行一些操作时应该开始:创建记录,更新记录等。这个工具帮助了我们很多。 hangfire.io 【参考方案1】:

我想这有点太晚了,但对于有同样问题的人来说..

你知道Quartz.NET和Hangfire.io吗?

也许这两者中的一个在您的情况下可能是一个非常有用的工具。 我在许多应用程序中都使用过它,从未遇到过任何问题。

例如,在 Quartz.Net 中,您首先必须通过创建一个实现 IJob 接口的类来创建一个“作业”(这是用于此类后台服务的术语):

public class HelloJob : IJob

    public async Task Execute(IJobExecutionContext context)
    
        await Console.Out.WriteLineAsync("Greetings from HelloJob!");
    

然后,您必须定义何时检查该作业,有很多方法(例如 CRON),但我们将在这里使用一个简单的时间表:

        StdSchedulerFactory factory = new StdSchedulerFactory();
        IScheduler scheduler = await factory.GetScheduler();

        await scheduler.Start();

        // define the job
        IJobDetail job = JobBuilder.Create<HelloJob>()
            .WithIdentity("job1", "group1")
            .Build();

        // Trigger the job to run now, and then repeat every 20 seconds
        ITrigger trigger = TriggerBuilder.Create()
            .WithIdentity("trigger1", "group1")
            .StartNow()
            .WithSimpleSchedule(x => x
                .WithIntervalInSeconds(20)
                .RepeatForever())
            .Build();

        // Tell quartz to schedule the job using our trigger, DON'T FORGET THIS ONE
        await scheduler.ScheduleJob(job, trigger);

您处于基于 Windows 服务的微服务架构中。您应该能够捕获应用程序的所有“正常”关闭。在这些情况下,您必须正确关闭 Quartz 调度程序:

await scheduler.Shutdown();

我个人非常喜欢这种方法。它是可重复使用的(您只需在此处安排一项新工作),并且对于您团队中的任何开发人员来说都很容易进入。

希望对你的问题有所帮助。

【讨论】:

是的,我会推荐 Hangfire 及其 BackgroundJob.Enqueue hangfire.io【参考方案2】:

ASP.NET 有一个用于运行后台任务的本机解决方案:HostingEnvironment.QueueBackgroundWorkItem 方法,可在 System.Web.Hosting 上找到。

它适用于同步或async 方法。与使用 ThreadPool 不同,QueueBackgroundWorkItem 确保 ASP.NET 运行时将延迟应用程序关闭,以防有任何待处理的工作项正在运行。

来自official documentation:

与普通的 ThreadPool 工作项不同,ASP.NET 可以跟踪通过此 API 注册的工作项当前正在运行的数量,并且 ASP.NET 运行时将尝试延迟 AppDomain 关闭,直到这些工作项完成执行.不能在 ASP.NET 管理的 AppDomain 之外调用此 API。提供的 CancellationToken 将在应用程序关闭时发出信号。

这是一个如何使用它的示例:

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using System.Web.Http;

namespace BackgroundWorkItemExample.Controllers

    public class HomeController : ApiController
    
        [HttpPost]
        public IHttpActionResult Execute()
        
            Debug.WriteLine("Doing something.");

            HostingEnvironment.QueueBackgroundWorkItem(ct => FireAndForgetMethodAsync());
            HostingEnvironment.QueueBackgroundWorkItem(ct => FireAndForgetMethod());
            HostingEnvironment.QueueBackgroundWorkItem(ct => FaultyFireAndForgetMethod());

            Debug.WriteLine("I don't care for that stuff running in the background. I'm out.");

            return Ok("It still returns, regardless of exceptions.");
        

        private async Task FireAndForgetMethodAsync()
        
            Debug.WriteLine("Started running an asynchronous background work item...");

            await Task.Delay(5000); // Pretend we are doing something for 5s

            Debug.WriteLine("Finished running that.");
        

        private void FireAndForgetMethod()
        
            Debug.WriteLine("Started running a background work item...");

            Thread.Sleep(5000); // Pretend we are doing something for 5s

            Debug.WriteLine("Finished running that.");
        

        private void FaultyFireAndForgetMethod()
        
            throw new Exception("I'm failing just to make a point.");
        
    

输出将是:

Doing something.
I don't care for that stuff running in the background. I'm out.
Started running a background work item...
Started running an asynchronous background work item...
Exception thrown: 'System.Exception' in WebApplication1.dll
An exception of type 'System.Exception' occurred in WebApplication1.dll but was not handled in user code
I'm failing just to make a point.
Finished running that.
Finished running that.

【讨论】:

【参考方案3】:

最安全的方法之一是使用线程。 您可以触发线程并让它运行。

如果您需要在应用关闭的情况下杀死线程,您可以添加 isBackground 参数。 否则,线程应该继续运行直到它的工作完成

Thread backgroundThread = new Thread(() => 

    Console.WriteLine("I'll run until the app Stops or I finish working");
    LongRunningJob();
, IsBackground = true);

Thread foregroundThread = new Thread(() => 

    Console.WriteLine("I'll stop when I'm finished");
    LongRunningJob();

backgroundThread.Start();
foregroundThread.Start()

【讨论】:

以上是关于用于后台任务的asp.net框架中的Ihostedservice等价物的主要内容,如果未能解决你的问题,请参考以下文章

Ktor 中的后台任务,例如 ASP.Net Core 中的“托管服务”

从 ASP.NET Core 中的控制器操作运行后台任务

在asp.net mvc 3中的后台作业/计划任务中发送电子邮件

2步轻松实现ASP.NET Core托管服务执行定时任务

.Net / Asp.Net Web api 后台任务

在高流量场景中使用 ASP.NET 中的 ThreadPool.QueueUserWorkItem