Quartz是一个开源的作业调度框架,OpenSymphony的开源项目。Quartz.Net 是Quartz的C#移植版本。
Scheduler 调度器。
IJob 任务,所有的定时作业都需要继承该类
IJobDetail 给定作业实例的详细属性
JobKey Quartz.IJobDetail的唯一标识
ITrigger 触发器,支持cron和简单基础时间
TriggerKey ITrigger 的唯一标识
例子
public class DayJobs : IJob
{
protected ILogger _logger;//日志,不做过多介绍
protected ILogger logger
{
get
{
if (_logger == null)
_logger = IocContainer.Resolve<ILoggerFactory>().Create(this.GetType());
return _logger;
}
}
/// <summary>
///
/// </summary>
public DayJobs()
{
_policySdk = new PolicySdkFactory();
}
/// <summary>
/// 执行,必须实现此方法
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Execute(IJobExecutionContext context)
{
try
{
//test
var test="test";//可打断点直接调试,不做过多处理
}
catch (Exception ex)
{
logger.Error("执行每日任务错误", this.GetType().FullName, ex);
}
await Task.FromResult(true);
}
}
在此之前需要引用相关nuget,Quartz,Quartz.Jobs,Quartz.Plugins,注意版本,需要对应项目net版本
基础配置可以直接于代码中配置,也可以读取配置文件数据,推荐使用配置文件。
//第一步:在web.config <configSections>中加入如下配置
<configSections>
<section name="quartz" type="System.Configuration.NameValueSectionHandler" />
</configSections>
//第二步:定义如下配置
<quartz>
<!-- 定时调度相关配置 -->
<!--可视化配置-->
<!--<add key="quartz.scheduler.exporter.port" value="555" />
<add key="quartz.scheduler.exporter.bindName" value="QuartzScheduler" />
<add key="quartz.scheduler.exporter.channelType" value="tcp" />
<add key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz" />-->
<!--******************************使用xml配置读取作业*********************************************-->
<add key="quartz.plugin.xml.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin,Quartz.Plugins" />
<add key="quartz.plugin.xml.fileNames" value="~/Configs/quartz_jobs.xml" /><!--相对路径-->
<!--<add key="quartz.scheduler.instanceId" value="AUTO"/>
<add key="quartz.scheduler.instanceName" value="调度监控系统"/>-->
<!--线程池--><!--
<add key="quartz.threadPool.type" value=""/>
--><!--线程数量--><!--
<add key="quartz.threadPool.threadCount" value=""/>
--><!--线程优先级--><!--
<add key="quartz.threadPool.threadPriority" value=""/>-->
</quartz>
//第三步 具体的作业和定时器定义 quartz_jobs.xml
<?xml version="1.0" encoding="utf-8" ?>
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
<processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives>
<schedule>
<!--开始执行一个调度-->
<!--任务,可添加多个-->
<job>
<!--任务名称【必填】:同一分组中多个作业的name唯一-->
<name>每日任务</name>
<!--任务所属分组【选填】-->
<group>调度</group>
<!--任务描述【选填】-->
<description>定时任务</description>
<!--任务类型【必填】:实现IJob接口,具体到命名空间的类名,程序集名(dll)-->
<job-type>Platform.Infrastructure.Schedulers.Jobs.DayJobs, Platform.Infrastructure</job-type>
<!--默认true-->
<durable>true</durable>
<!--默认false-->
<recover>false</recover>
</job>
<!--触发器,可添加多个-->
<trigger>
<!--simple:简单触发器类型,cron:复杂触发器类型-->
<cron>
<!--触发器名称【必填】:通与ob一样,同分组中也是唯一的-->
<name>日触发器</name>
<!--触发器组【选填】-->
<group>触发器</group>
<!--触发器描述【选填】-->
<description>每天的23:30发生</description>
<!--要调度的任务名称【必填】:必须和对应job节点中的name-->
<job-name>每日任务</job-name>
<!--要调度的任务的所属分组【选填】:必须和对应job节点中的group-->
<job-group>调度</job-group>
<cron-expression>0 30 23 * * ? </cron-expression><!--在线生成cron地址https://cron.qqe2.com/-->
</cron>
</trigger>
<!--结束一个调度-->
</schedule>
</job-scheduling-data>
定义
public class SchedulerBase
{
/// <summary>
/// 调度器基础配置
/// </summary>
public static Task<IScheduler> Scheduler
{
get
{
//已经调整为读取配置文件
NameValueCollection props = new NameValueCollection();
props["quartz.scheduler.instanceId"] = Configs.GetValue("quartz.scheduler.instanceId");
props["quartz.scheduler.instanceName"] = Configs.GetValue("quartz.scheduler.instanceName");
//可视化端口
//props["quartz.scheduler.exporter.port"] = Configs.GetValue("quartz.scheduler.exporter.port");
//props["quartz.scheduler.exporter.bindName"] = Configs.GetValue("quartz.scheduler.exporter.bindName");
//props["quartz.scheduler.exporter.channelType"] = Configs.GetValue("quartz.scheduler.exporter.channelType"); //协议类型
//props["quartz.scheduler.exporter.type"] = Configs.GetValue("quartz.scheduler.exporter.type");
var scheduler = StdSchedulerFactory.GetDefaultScheduler();
return scheduler;
}
}
public static ITrigger AddTrigger(SchedulerReq model)
{
ITrigger dayJobTrigger = TriggerBuilder.Create()
.WithIdentity(model.TriggerName, model.TriggerGroup)
.WithDescription(model.TriggerDescription)
.StartNow()
.WithCronSchedule(model.CronSchedule)
.Build();
return dayJobTrigger;
}
}
public class SchedulerMain
{
private static IScheduler scheduler;
/// <summary>
/// 定时调度任务
/// </summary>
public SchedulerMain()
{
}
public void Start()
{
Init().Wait();
}
public async Task Init()
{
scheduler = await SchedulerBase.Scheduler;
// await scheduler.GetJobGroupNames();
//启动调度器
await scheduler.Start();
//执行调度
//await SchedulerBase.AddSchedule(
// new Jobs.JobServer<Jobs.DayJobs>(),
// new Triggers.DayTrigger().SendTrigger(),
// "每日任务", "调度");
}
public void Stop()
{
Shutdown().Wait();
}
public async Task Shutdown()
{
if (scheduler != null)
await scheduler.Shutdown();
}
public static IScheduler GetScheduler()
{
return scheduler;
}
}
启用
Global.asax中的Application_Start中进行Start,就完成了基础的使用
protected void Application_Start()
{
New SchedulerMain().Start();
}
/// <summary>
/// 请求参数
/// </summary>
public class SchedulerReq
{
/// <summary>
/// 作业名称
/// </summary>
public string JobName { get; set; }
/// <summary>
/// 作业分组
/// </summary>
public string JobGroup { get; set; }
/// <summary>
/// 间隔(Cron)
/// </summary>
public string CronExpression { get; set; }
/// <summary>
/// 调度器描述
/// </summary>
public string TriggerDescription { get; set; }
/// <summary>
/// 调度器名称
/// </summary>
public string TriggerName { get; set; }
/// <summary>
/// 调度器所属分组
/// </summary>
public string TriggerGroup { get; set; }
/// <summary>
/// 运行频率
/// </summary>
public string CronSchedule { get; set; }
/// <summary>
/// 程序集
/// </summary>
public string AssemblyName { get; set; }
/// <summary>
/// 命名空间加job所在类名
/// </summary>
public string ClassName { get; set; }
}
/// <summary>
/// 结果
/// </summary>
public class SchedulerResult
{
/// <summary>
/// 作业名称
/// </summary>
public string JobName { get; set; }
/// <summary>
/// 作业分组
/// </summary>
public string JobGroup { get; set; }
/// <summary>
/// 作业描述
/// </summary>
public string JobDescription { get; set; }
/// <summary>
/// 创建
/// </summary>
public DateTime? CreateTime { get; set; }
/// <summary>
/// 最后执行时间
/// </summary>
public DateTime? LastRunTime { get; set; }
/// <summary>
/// 下次执行时间
/// </summary>
public DateTime? NextRunTime { get; set; }
/// <summary>
/// 间隔(Cron)
/// </summary>
public string CronExpression { get; set; }
/// <summary>
/// 调度器描述
/// </summary>
public string TriggerDescription { get; set; }
/// <summary>
/// 调度器名称
/// </summary>
public string TriggerName { get; set; }
/// <summary>
/// 调度器所属分组
/// </summary>
public string TriggerGroup { get; set; }
/// <summary>
/// 任务当前状态
/// </summary>
public string TriggerState { get; set; }
/// <summary>
/// 程序集
/// </summary>
public string AssemblyName { get; set; }
/// <summary>
/// 命名空间加job所在类名
/// </summary>
public string ClassName { get; set; }
}
暂未关联数据库,后期会逐步与数据库关联
public class SchedulerApi
{
private static IScheduler scheduler;
public SchedulerApi()
{
scheduler = SchedulerBase.Scheduler.Result;
}
/// <summary>
/// 获取任务列表
/// </summary>
/// <returns></returns>
public async Task<List<SchedulerResult>> GetJobs()
{
var list = new List<SchedulerResult>();
var groups = await scheduler.GetJobGroupNames();
foreach (var groupName in groups)
{
foreach (var jobKey in await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)))
{
var taskOptions = new SchedulerResult();
var job = scheduler.GetJobDetail(jobKey);
taskOptions.JobDescription = job.Result.Description;
taskOptions.JobName = jobKey.Name;
taskOptions.JobGroup = jobKey.Group;
taskOptions.AssemblyName = job.Result.JobType.Assembly.GetName().Name;
taskOptions.ClassName = job.Result.JobType.FullName;
var triggers = await scheduler.GetTriggersOfJob(jobKey);
foreach (CronTriggerImpl trigger in triggers)
{
//最后执行时间
DateTimeOffset? dateTimePreviousOffset = trigger.GetPreviousFireTimeUtc();
//下次执行时间
DateTimeOffset? dateTimeNextOffset = trigger.GetNextFireTimeUtc();
DateTimeOffset? createTime = trigger.StartTimeUtc;
if (dateTimePreviousOffset != null)
taskOptions.LastRunTime = TimeZoneInfo.ConvertTimeFromUtc(dateTimePreviousOffset.Value.DateTime, TimeZoneInfo.Local);
if (dateTimeNextOffset != null)
taskOptions.NextRunTime = TimeZoneInfo.ConvertTimeFromUtc(dateTimeNextOffset.Value.DateTime, TimeZoneInfo.Local);
if (createTime != null)
taskOptions.CreateTime = TimeZoneInfo.ConvertTimeFromUtc(createTime.Value.DateTime, TimeZoneInfo.Local);
taskOptions.TriggerDescription = trigger.Description;
taskOptions.TriggerName = trigger.Key.Name;
taskOptions.TriggerGroup = trigger.Key.Group;
taskOptions.TriggerState = ((ETriggerState)(int)scheduler.GetTriggerState(trigger.Key).Result).ToString();
taskOptions.CronExpression = trigger.CronExpressionString;
list.Add(taskOptions);
}
}
}
return list;
}
/// <summary>
/// 添加任务(可自己进行部分修改,例如使用异步,返回不同参数等)
/// </summary>
/// <param name="model"></param>
public void AddJobs(SchedulerReq model)
{
if (!ValidExpression(model.CronSchedule)) Ensure.Error("运行频率,【Cron】表达式设置错误!");
ITrigger trigger = SchedulerBase.AddTrigger(model);
JobKey jk = new JobKey(model.JobName, model.JobGroup);
if (scheduler.CheckExists(jk).Result)
{
Ensure.Error($"任务{model.JobName}已存在!");
}
else
{
Assembly assembly = Assembly.Load(model.AssemblyName);
Type type = assembly.GetType(model.ClassName);
if (type == null) Ensure.Error("Job类错误!");
IJobDetail job = JobBuilder.Create(type).WithIdentity(model.JobName, model.JobGroup).Build();
scheduler.ScheduleJob(job, trigger);
}
}
/// <summary>
/// 删除任务
/// </summary>
/// <param name="model"></param>
public void DeleteJob(SchedulerReq model)
{
ITrigger trigger = SchedulerBase.AddTrigger(model);
JobKey jk = new JobKey(model.JobName, model.JobGroup);
if ( scheduler.CheckExists(jk).Result)
{
scheduler.DeleteJob(jk);
}
else
{
Ensure.Error($"任务{model.JobName}不存在!");
}
}
/// <summary>
/// 继续任务
/// </summary>
/// <param name="model"></param>
public void ResumeJob(SchedulerReq model)
{
JobKey jk = new JobKey(model.JobName, model.JobGroup);
if ( scheduler.CheckExists(jk).Result)
{
//任务已经存在则继续任务
scheduler.ResumeJob(jk);
}
}
/// <summary>
/// 暂停任务
/// </summary>
/// <param name="model"></param>
public void PauseJob(SchedulerReq model)
{
JobKey jk = new JobKey(model.JobName, model.JobGroup);
if ( scheduler.CheckExists(jk).Result)
{
//任务已经存在则暂停任务
scheduler.PauseJob(jk);
}
}
/// <summary>
/// 更新任务
/// </summary>
/// <param name="model"></param>
public void UpdateJob(SchedulerReq model)
{
JobKey jk = new JobKey(model.JobName, model.JobGroup);
if (scheduler.CheckExists(jk).Result)
{
var triggers = scheduler.GetTriggersOfJob(jk).Result;
ITrigger trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == model.TriggerName).FirstOrDefault();
scheduler.PauseTrigger(trigger.Key);
scheduler.UnscheduleJob(trigger.Key);// 移除触发器
scheduler.DeleteJob(jk);
AddJobs(model);
}
}
/// <summary>
/// 立即执行
/// </summary>
/// <param name="model"></param>
public void RunJob(SchedulerReq model)
{
JobKey jk = new JobKey(model.JobName, model.JobGroup);
if (scheduler.CheckExists(jk).Result)
{
//任务已经存在则立即执行
scheduler.TriggerJob(jk);
}
}
/// <summary>
/// 启动任务调度
/// </summary>
/// <param name="model"></param>
public async void StartSchedule()
{
if (!scheduler.IsStarted)
{
//等待任务运行完成
await scheduler.Start();
}
}
/// <summary>
/// 停止任务调度
/// </summary>
/// <param name="model"></param>
public async void StopSchedule()
{
if (!scheduler.IsShutdown)
{
//等待任务运行完成
await scheduler.Shutdown(false);
}
}
/// <summary>
/// 校验字符串是否为正确的Cron表达式
/// </summary>
/// <param name="cronExpression">带校验表达式</param>
/// <returns></returns>
public bool ValidExpression(string cronExpression)
{
return CronExpression.IsValidExpression(cronExpression);
}
/// <summary>
/// 验证是否存在任务
/// </summary>
/// <param name="name"></param>
/// <param name="group"></param>
/// <returns></returns>
public bool CheckExists(string name, string group)
{
JobKey jk = new JobKey(name, group);
return scheduler.CheckExists(jk).Result;
}
}
直接在控制器中使用即可,只例举获取,其它接口同
public class SchedulerController:BaseController{
private readonly SchedulerApi _schedulerApi;
public SchedulerController()
{
_schedulerApi = new SchedulerApi();
}
/// <summary>
/// 获取任务列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetJobs")]
[SwaggerResponse(200, "成功返回参数", type: typeof(SchedulerResult))]
public IHttpActionResultGetJobs() {
var list = _schedulerApi.GetJobs().Result;
return Success(list);
}
}
参考链接:
https://www.cnblogs.com/zixuan9527/p/8601151.html
https://github.com/weizhong1988/Weiz.TaskManager/blob/master/Weiz.TaskManager.TaskUtility/Quartz/QuartzHelper.cs