温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何掌握Quartz.net分布式定时任务的姿势

发布时间:2021-11-18 16:01:47 阅读:233 作者:柒染 栏目:大数据
开发者测试专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

这篇文章给大家介绍如何掌握Quartz.net分布式定时任务的姿势,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

引言

长话短说,今天聊一聊分布式定时任务,我的流水账笔记:

  • ASP.NET Core+Quartz.Net实现web定时任务    
  • AspNetCore结合Redis实践消息队列    

细心朋友稍一分析,就知道还有问题:
水平扩展后的WebApp的Quartz.net定时任务会多次触发, 因为webapp实例使用的是默认的RAMJobStore, 多实例在内存中都维护了Job和Trigger的副本.

我的定时任务是同步任务,多次执行倒是没有太大问题,但对于特定业务的定时任务, 多次执行可能是致命问题。

基于此,来看看Quartz.net 分布式定时任务的姿势

 

AdoJobStore

很明显,水平扩展的多实例需要一个 独立于web实例的机制来存储Job和Trigger.

Quartz.NET提供ADO.NET JobStore来存储任务数据。

  1. 先使用SQL脚本在数据库中生成指定的表结构

执行脚本之后,会看到数据库中多出几个以 QRTZ_开头的表

  1. 配置Quartz.net使用AdoJobStore

可采用编码形式或者 quartz.config形式添加配置

 

快速实践

 
1. 预先生成Job、Trigger表

从https://github.com/quartznet/quartznet/tree/master/database/tables 下载合适的数据库表脚本, 生成指定的表结构

 
2. 添加AdoJobStore

本次使用编码方式添加AdoJobStore配置。
首次启动会将代码中Job和Trigger持久化到sqlite,后面就直接从sqlite中加载Job和Trigger

using System;using System.Collections.Specialized;using System.Data;using System.Threading.Tasks;using Microsoft.Data.Sqlite;using Microsoft.Extensions.Logging;using Quartz;using Quartz.Impl;using Quartz.Impl.AdoJobStore.Common;using Quartz.Spi;namespace EqidManager{    using IOCContainer = IServiceProvider;    public class QuartzStartup    {        public IScheduler Scheduler { get; set; }        private readonly ILogger _logger;        private readonly IJobFactory iocJobfactory;        public QuartzStartup(IOCContainer IocContainer, ILoggerFactory loggerFactory)        {            _logger = loggerFactory.CreateLogger<QuartzStartup>();            iocJobfactory = new IOCJobFactory(IocContainer);            DbProvider.RegisterDbMetadata("sqlite-custom", new DbMetadata()            {                AssemblyName = typeof(SqliteConnection).Assembly.GetName().Name,                ConnectionType = typeof(SqliteConnection),                CommandType = typeof(SqliteCommand),                ParameterType = typeof(SqliteParameter),                ParameterDbType = typeof(DbType),                ParameterDbTypePropertyName = "DbType",                ParameterNamePrefix = "@",                ExceptionType = typeof(SqliteException),                BindByName = true            });            var properties = new NameValueCollection            {                ["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",                ["quartz.jobStore.useProperties"] = "true",                ["quartz.jobStore.dataSource"] = "default",                ["quartz.jobStore.tablePrefix"] = "QRTZ_",                ["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SQLiteDelegate, Quartz",                ["quartz.dataSource.default.provider"] = "sqlite-custom",                ["quartz.dataSource.default.connectionString"] = "Data Source=EqidManager.db",                ["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz",                ["quartz.serializer.type"] = "binary"            };            var schedulerFactory = new StdSchedulerFactory(properties);            Scheduler = schedulerFactory.GetScheduler().Result;            Scheduler.JobFactory = iocJobfactory;        }        public async Task<IScheduler> ScheduleJob()        {            var _eqidCounterResetJob = JobBuilder.Create<EqidCounterResetJob>()              .WithIdentity("EqidCounterResetJob")              .Build();            var _eqidCounterResetJobTrigger = TriggerBuilder.Create()                .WithIdentity("EqidCounterResetCron")                .StartNow()                //每天凌晨0s                .WithCronSchedule("0 0 0 * * ?")      Seconds,Minutes,Hours,Day-of-Month,Month,Day-of-Week,Year(optional field)                .Build();                    // 这里一定要先判断是否已经从SQlite中加载了Job和Trigger            if (!await Scheduler.CheckExists(new JobKey("EqidCounterResetJob")) &&                !await Scheduler.CheckExists(new TriggerKey("EqidCounterResetCron")))            {                await Scheduler.ScheduleJob(_eqidCounterResetJob, _eqidCounterResetJobTrigger);            }                        await Scheduler.Start();            return Scheduler;        }        public void EndScheduler()        {            if (Scheduler == null)            {                return;            }            if (Scheduler.Shutdown(waitForJobsToComplete: true).Wait(30000))                Scheduler = null;            else            {            }            _logger.LogError("Schedule job upload as application stopped");        }    }}
 

上面是Quartz.NET 从sqlite中加载Job和Trigger的核心代码

这里要提示两点:

①  IOCJobFactory 是自定义JobFactory,目的是与ASP.NET Core原生依赖注入结合
② 在调度任务的时候,先判断是否已经从sqlite加载了Job和Trigger

 
3.添加Quartz.Net UI轮子

附赠Quartz.NET的调度UI: CrystalQuartz, 方便在界面管理和调度任务
① Install-Package CrystalQuartz.AspNetCore -IncludePrerelease
② Startup启用CrystalQuartz

using CrystalQuartz.AspNetCore;/* * app is IAppBuilder * scheduler is your IScheduler (local or remote) */var quartz = app.ApplicationServices.GetRequiredService<QuartzStartup>();var _schedule = await  quartz.ScheduleJob();app.UseCrystalQuartz(() => scheduler);
 

③ 在localhost:YOUR_PORT/quartz地址查看调度

如何掌握Quartz.net分布式定时任务的姿势    

关于如何掌握Quartz.net分布式定时任务的姿势就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:https://my.oschina.net/u/3791544/blog/4353963

AI

开发者交流群×