.NET Core实战使用Quartz.Net
使用Quartz.Net之前先了解一些基本概念、接口以及类。基本概念Scheduler: 用来管理调度,实现IScheduler接口。Job: 具体的作业,实现IJob接口,通过IJob的Execute方法来做具体的事,比如使用HttpClient向一个业务接口发送请求。Trigger: 用来触发Job开始工作,实现ITrigger接口。基本接口和类IScheduler: 包含了调度的主要APII
·
使用Quartz.Net
之前先了解一些基本概念、接口以及类。
基本概念
Scheduler
: 用来管理调度,实现IScheduler
接口。Job
: 具体的作业,实现IJob
接口,通过IJob
的Execute
方法来做具体的事,比如使用HttpClient
向一个业务接口发送请求。Trigger
: 用来触发Job
开始工作,实现ITrigger
接口。
基本接口和类
IScheduler
: 包含了调度的主要APIIJob
: 需要IScheduler
执行的具体工作IJobDetail
: 用来生成Job
的实例ITrigger
: 触发器,决定了在什么时间以何种方式触发JobBuilder
: 用来生成IJobDetail
实例TriggerBuilder
: 用来生成ITrigger
实例ISchedulerFactory
:用来生成IScheduler
实例IJobListener
: 侦听IJob
在项目中使用的大致步骤是: 在mysql数据库运行sql语句生成Quartz.Net相关的、专属的数据库,从NuGet下载相关组件,然后在
Startup.cs
的DI容器和请求管道中分别配置,写一个围绕IScheduler
的帮助类,定义IJob
如何实现,最后在控制器中调用IScheduler
的帮助类。
在mysql数据库运行sql语句
# By: Ron Cordell - roncordell
# I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.
# make sure you have UTF-8 collaction for best .NET interoperability
# CREATE DATABASE quartznet CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(60) NOT NULL,
JOB_NAME VARCHAR(60) NOT NULL,
JOB_GROUP VARCHAR(60) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE BOOLEAN NOT NULL,
IS_NONCONCURRENT BOOLEAN NOT NULL,
IS_UPDATE_DATA BOOLEAN NOT NULL,
REQUESTS_RECOVERY BOOLEAN NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(60) NOT NULL,
TRIGGER_NAME VARCHAR(60) NOT NULL,
TRIGGER_GROUP VARCHAR(60) NOT NULL,
JOB_NAME VARCHAR(60) NOT NULL,
JOB_GROUP VARCHAR(60) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(19) NULL,
PREV_FIRE_TIME BIGINT(19) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(19) NOT NULL,
END_TIME BIGINT(19) NULL,
CALENDAR_NAME VARCHAR(60) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(60) NOT NULL,
TRIGGER_NAME VARCHAR(60) NOT NULL,
TRIGGER_GROUP VARCHAR(60) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(60) NOT NULL,
TRIGGER_NAME VARCHAR(60) NOT NULL,
TRIGGER_GROUP VARCHAR(60) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(60) NOT NULL,
TRIGGER_NAME VARCHAR(60) NOT NULL,
TRIGGER_GROUP VARCHAR(60) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 BOOLEAN NULL,
BOOL_PROP_2 BOOLEAN NULL,
TIME_ZONE_ID VARCHAR(80) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(60) NOT NULL,
TRIGGER_NAME VARCHAR(60) NOT NULL,
TRIGGER_GROUP VARCHAR(60) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(60) NOT NULL,
CALENDAR_NAME VARCHAR(60) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(60) NOT NULL,
TRIGGER_GROUP VARCHAR(60) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(60) NOT NULL,
ENTRY_ID VARCHAR(140) NOT NULL,
TRIGGER_NAME VARCHAR(60) NOT NULL,
TRIGGER_GROUP VARCHAR(60) NOT NULL,
INSTANCE_NAME VARCHAR(60) NOT NULL,
FIRED_TIME BIGINT(19) NOT NULL,
SCHED_TIME BIGINT(19) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(60) NULL,
JOB_GROUP VARCHAR(60) NULL,
IS_NONCONCURRENT BOOLEAN NULL,
REQUESTS_RECOVERY BOOLEAN NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(60) NOT NULL,
INSTANCE_NAME VARCHAR(60) NOT NULL,
LAST_CHECKIN_TIME BIGINT(19) NOT NULL,
CHECKIN_INTERVAL BIGINT(19) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(60) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
commit;
从NuGet下载相关组件
Quartz
Quartz.Serialization.Json
在Startup.cs
的DI容器和请求管道中分别配置
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//基本配置
NameValueCollection props = new NameValueCollection {
{ "quartz.threadPool.threadCount","100"},
{ "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},
{ "quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz"},
{ "quartz.jobStore.dataSource","myDS"},
{ "quartz.dataSource.myDS.connectionString","Server=your_ip;Port=3306;Database=quartznet;Uid=your_username;Pwd=your_password"},
{ "quartz.dataSource.myDS.provider","MySql"},
{ "quartz.serializer.type","json"}
//{ "quartz.jobStore.useProperties","true"}
};
var schedulerFactory = new StdSchedulerFactory(props);
var scheduler = schedulerFactory.GetScheduler().Result;
//这里定义了一个IJobListener,用来处理当Job运行失败
var yourContext = services.BuildServiceProvider().GetService<YourContext>();
IJobListener jobListener = new JobFailureHandler(yourContext);
scheduler.ListenerManager.AddJobListener(jobListener);
services.AddSingleton<ISchedulerFactory>(schedulerFactory);
services.AddSingleton(scheduler);
//SimpleInjectorJobFactory,下面详细写
services.AddSingleton<IJobFactory, SimpleInjectorJobFactory>();
services.AddSingleton<IJobListener, JobFailureHandler>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
{
//JobScheduler,是围绕`IScheduler`的帮助类,下面详细写
var scheduler = serviceProvider.GetService<JobScheduler>();
scheduler.Init();
}
围绕IScheduler
的帮助类
public class JobScheduler
{
private static IScheduler _scheduler;
public JobScheduler(IScheduler scheduler)
{
_scheduler = scheduler;
}
public void Start()
{
if (!_scheduler.IsStarted)
{
_scheduler.Start();
}
}
public void Shutdown(bool waitForJobsToComplete)
{
if (!_scheduler.IsShutdown)
{
_scheduler.Shutdown(waitForJobsToComplete);
}
}
public void StandBy()
{
if (!_scheduler.InStandbyMode)
{
_scheduler.Standby();
}
}
public void PauseAll()
{
_scheduler.PauseAll();
}
public void ResumeAll()
{
_scheduler.ResumeAll();
}
public async Task<bool> UnscheduleJob(string triggerName, string triggerGroup)
{
return await _scheduler.UnscheduleJob(new TriggerKey(triggerName, triggerGroup));
}
public Task<bool> DeleteJob(string jobName, string jobGroup)
{
return _scheduler.DeleteJob(new JobKey(jobName, jobGroup));
}
public void PauseJob(string jobName, string groupName)
{
_scheduler.PauseJob(new JobKey(jobName, groupName));
}
public void ResumeJob(string jobName, string groupName)
{
_scheduler.ResumeJob(new JobKey(jobName, groupName));
}
public void TriggerJob(string jobName, string groupName)
{
_scheduler.TriggerJob(new JobKey(jobName, groupName));
}
public Task<bool> IsJobExist(string jobName, string groupName)
{
return _scheduler.CheckExists(new JobKey(jobName, groupName));
}
public async Task AddJob(string your_string, string group, string cron)
{
//JobUtil帮助类下面详细说明
if (!JobUtil.IsValidCronExpression(cron.Trim()))
throw new ArgumentException(string.Format("invalid expression{0}", cron));
if(await IsJobExist(your_string, group))
{
await DeleteJob(your_string, group);
}
var jobDetail = JobBuilder.Create()
.OfType(typeof(HttpRequestJob)) //这里的HttpRequestJob就是实现IJob的类,定义具体做什么,下面详细说
.WithIdentity(your_string, group)
.StoreDurably(true)
.RequestRecovery(false)
.UsingJobData("your_key", your_string) //这里可以放一些key, value键值对,在IJob中可以调用
.Build();
var triggerBuilder = TriggerBuilder.Create()
.WithIdentity(your_string, group)
.WithDailyTimeIntervalSchedule(s =>
{ //解决在IIS上不生效的问题
s.WithIntervalInHours(24)
.OnEveryDay()
.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(0, 0))
.InTimeZone(TimeZoneInfo.Local);
})
.WithCronSchedule(cron.Trim(), x => x.WithMisfireHandlingInstructionDoNothing());//注意WithMisfireHandlingInstructionDoNothing,这里关系这执行错过用什么策略,是枚举值,根据业务场景来设置
var trigger = triggerBuilder.Build();
await _scheduler.ScheduleJob(jobDetail, trigger);
}
}
<!--JobUtil帮助类-->
public static class JobUtil
{
//判断cron表达式是否有效
public static bool IsValidCronExpression(string expression)
{
return CronExpression.IsValidExpression(expression);
}
//根据星期、小时、分钟、秒生成cron表达式
public static string GetCronByTime(int hour, int minute, int second, TimingWeekEnum week)
{
string cron = string.Empty;
//string cron = "0 0 12 ? * SUN *";
if (week == TimingWeekEnum.Monday)
{
cron = $"{second} {minute} {hour} ? * MON *";
}
else if (week == TimingWeekEnum.Tuesday)
{
cron = $"{second} {minute} {hour} ? * TUE *";
}
else if (week == TimingWeekEnum.Wednesday)
{
cron = $"{second} {minute} {hour} ? * WED *";
}
else if (week == TimingWeekEnum.Thursday)
{
cron = $"{second} {minute} {hour} ? * THU *";
}
else if (week == TimingWeekEnum.Friday)
{
cron = $"{second} {minute} {hour} ? * FRI *";
}
else if (week == TimingWeekEnum.Saturday)
{
cron = $"{second} {minute} {hour} ? * SAT *";
}
else
{
cron = $"{second} {minute} {hour} ? * SUN *";
}
return cron;
}
}
定义IJob
如何实现
public class HttpRequestJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
//这个就是在JobScheduler帮助类的AddJob方法中UsingJobData("your_key", your_string)这里的设置
string some_str = context.JobDetail.JobDataMap.GetString("your_key");
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(some_seconds);
string url = your_api_url + "/" + some_str;
var message = await client.GetAsync(url);
}
}
}
然后在your_api_url这个api中定义如何实现。
在控制器中调用IScheduler
的帮助类进行Quartz.Net
相关设置
public class SomeController : Controller
{
private readonly JobScheduler _scheduler;
public SomeController(JobScheduler scheduler)
{
_scheduler = scheduler;
}
}
SimpleInjectorJobFactory
这个类
这个类可能最终没用上。
public class SimpleInjectorJobFactory : IJobFactory
{
private readonly IServiceProvider service;
public SimpleInjectorJobFactory(IServiceProvider serviceProvider)
{
this.service = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return (IJob)service.GetService(bundle.JobDetail.JobType);
}
public void ReturnJob(IJob job)
{
var disposable = job as IDisposable;
disposable?.Dispose();
}
}
更多推荐
已为社区贡献24条内容
所有评论(0)