使用Quartz.Net达成Asp.Net Core长时程执行

Web应用程式本身的机制并不适合用来作为执行需要长时程运行的需求,而这类需求却很常见,而常见的解决方式是

加长Timeout时间。使用Queue的机制于背景执行。

第1种方式并不是一种正确的解决方式,严格来说不应该这样做。
第2种方式很容易达成,但有个缺点,使用者无法得知执行时的状态,因此无法确认到底是失败了还是仍然在执行中,这个问题可以透过一些设计解决,但都非常複杂。
目前在Asp.Net Core已经提供了「託管服务」IHostedService,可以解决Web本身不适合做为长时程执行的问题,然而单靠「託管服务」要实现整体的完整机制并不容易,而Quaerz.Net这个老牌的排程程式库能弭补这个问题,目前新版的Quaerz.Net 3已与Asp.Net Core已有很好的整合。

底下教学目标达成下列需求

允许Task于背景运行。能将执行过程中回报至UI上。

完整的程式可由此下载

初始化Quartz.Net

首先透过nuget将Quartz.Net引用至专案

Install-Package Quartz.AspNetCore

修改Startup.cs加入Quartz.Net的初始化相关程式

        public void ConfigureServices(IServiceCollection services)        {            services.AddControllersWithViews();            services.AddQuartz(q =>            {                q.UseMicrosoftDependencyInjectionScopedJobFactory();                q.UseSimpleTypeLoader();                q.UseInMemoryStore();                q.UseDefaultThreadPool(tp =>                {                    tp.MaxConcurrency = 10;                });                var jobKey = DemoJob.JobKey;                q.AddJob<DemoJob>(jobKey, j => j                    .StoreDurably()                    .WithDescription("demo job")                );            });            // ASP.NET Core hosting            services.AddQuartzServer(options =>            {                options.WaitForJobsToComplete = true;            });        }

这里为了展示只简单的使用一些基本配置,想要更深入的了解Quartz.Net可以参考官方的quick start。
上面程式中有个地方须注意因为这边的DemoJob并没有关联到任何的Trigger,Quartz.Net预设情况下会自动删除因而会出现错误,因此要加上StoreDurably()。

撰写Job

    public class DemoJob : IJob    {        public readonly static JobKey JobKey = new JobKey(nameof(DemoJob), JobKey.DefaultGroup);        public async Task Execute(IJobExecutionContext context)        {            await Task.Run(() =>            {                for (var i = 0; i < 5; i++)                {                    var message = $"第{i}次执行";                    Console.WriteLine(message);                    context.Result = message;                    context.CancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));                }            }, context.CancellationToken);        }    }

DemoJob作为展示,单纯的透过Console.WriteLine输出执行状态到Asp.Net core的Host上,因此如果使用IIS作为Host画面上是不会看到任何输出,所以记得要切换至专案作为Host
http://img2.58codes.com/2024/20134249xNS3VuFIu7.jpg
上面程式中的context.Result为Quartz.Net中提供的回报执行状态机制,后面我们会透过这个参数来取得目前Job中的执行状态,其物件形态为Object也就是说可以自定义想需要的内容,这边单纯的字串来回报。

新增TaskController与NewTask方法,用来建立Task的Trigger,让UI触发执行。

        public async Task<IActionResult> NewTask([FromServices] ISchedulerFactory schedulerFactory, CancellationToken cancellationToken)        {            var scheduler = await schedulerFactory.GetScheduler(cancellationToken);            var trigger = TriggerBuilder.Create()                .ForJob(DemoJob.JobKey)                .StartNow()                .WithSimpleSchedule(x => x.WithRepeatCount(0))                .Build();            await scheduler.ScheduleJob(trigger);            return RedirectToAction("ExecutingJobs");        }

因为Quartz.Net本身目的是排程功能,而我们希望的Task只是单纯的执行一次,因此Trigger使用Simple并且设定WithRepeatCount为0,那么Trigger执行后即会被Quartz.Net删除。

最后提供一个UI用来显示目前执行中的状态

        public async Task<IActionResult> ExecutingJobs([FromServices] ISchedulerFactory schedulerFactory, CancellationToken cancellationToken)        {            var scheduler = await schedulerFactory.GetScheduler();            var executingJobs = await scheduler.GetCurrentlyExecutingJobs(cancellationToken);            var data = executingJobs.Select(x => new JobExecutionContextModel(x));            return View(data);        }

这样就简单快速的完成了一个可以在WEB长时程执行的机制并且能够有UI反馈
http://img2.58codes.com/2024/20134249hWb2KkVKC7.jpg

当然Quartz.Net能做的不止于此,譬如说执行中的Task允许取消机制,实现如Windows排程功能,并且可以让使用透过UI来操作,集群机制等等。


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章