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

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

  1. 加长Timeout时间。
  2. 使用Queue的机制於背景执行。

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

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

  1. 允许Task於背景运行。
  2. 能将执行过程中回报至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
https://ithelp.ithome.com.tw/upload/images/20210112/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反馈
https://ithelp.ithome.com.tw/upload/images/20210112/20134249hWb2KkVKC7.jpg

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


<<:  ASUS Zenfone 3 Android 8.0.0 绕过 Google FRP

>>:  [分享] verilog设计经验总结

[Day1] 何谓自然语言处理

其实google的话就会有很多相关自然语言处理的定义与文章,自然语言处理英文为Natural Lan...

[Day 29]TensorFlow矩阵运算

今天笔者想聊聊TensorFlow的矩阵运算模式,笔者向来的数学都不是太好,此次30天的尾声想要与大...

[Day3] 经典时间序列预测方法盘点

第一篇记录了时间序列属性,将趋势、季节性等元素拆解、分别画出图表; 第二篇则介绍时间序列转换方法,透...

Day30 :【TypeScript 不用学了】我终於完赛了!

终於完赛了!最近跑去画画,附上一幅《小王子与小狐狸》,小王子里面,最喜欢的就是他们的故事,是说画画...

Day 28: 人工智慧在音乐领域的应用 (伦敦-Jukedeck、纽约-Amper Music 、OpenAI-Jukebox)

今天我们继续介绍一些比较知名的AI作曲的公司/软件。 Jukedeck Jukedeck可以说是AI...