Quartz.net的使用——显示任务信息、暂停、恢复、显示下次触发时间、禁止并发运行
Quartz.net的使用——显示任务信息、暂停、恢复、显示下次触发时间、禁止并发运行
最近在使用Quartz.net开发项目的时候,因为业务需求,我需要在页面进行任务的管理,比如暂停、恢复、显示下次执行时间等。网上关于C#中的Quartz的信息很少,因此我很多是通过查找Java的API完成的,如果大家需要其它需求,也可以直接查找Java中Quartz怎么使用的就行。
这里我使用的是基于配置文件的方式进行的,对于Quartz.net不熟悉的可以看我的其它博客,这里我就不多讲了,直接开始上代码。
我这里建立一个Windows窗体应用,窗体类命名为:MainView。
首先建立两个Job类,其中的MainView mainView = MainView.GetInstance();mainView.JobList(); 先不用管,之后我会解释为什么用这个,我这里每10秒触发一次Job,然后Job1执行至少需要15秒,为了让其不并发执行,加入了[DisallowConcurrentExecutionAttribute]属性,我理解这东西类似于Java中的注解:
-
[DisallowConcurrentExecutionAttribute]
-
public class Job1:IJob
-
{
-
public void Execute(IJobExecutionContext context)
-
{
-
MainView mainView = MainView.GetInstance();
-
mainView.JobList();
-
//模拟运行该任务需要15秒
-
Thread.Sleep(15000);
-
MessageBox.Show("第一个Job:" + DateTime.Now.ToString());
-
}
-
}
-
public class Job2:IJob
-
{
-
public void Execute(IJobExecutionContext context)
-
{
-
MainView mainView = MainView.GetInstance();
-
mainView.JobList();
-
MessageBox.Show("第二个Job:" + DateTime.Now.ToString());
-
}
-
}
接着编写Quartz的配置文件quartz_jobs.xml,每10秒运行一次:
-
<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>Job1</name>
-
<!--组名,随便取-->
-
<group>group1</group>
-
<!--描述-->
-
<description>job1</description>
-
<!--作业类,要定位到我们代码中创建的作业,命名空间.类名-->
-
<job-type>QuartzTest.Job1, QuartzTest</job-type>
-
<durable>true</durable>
-
<recover>false</recover>
-
</job>
-
<trigger>
-
<!--cron表达式触发器-->
-
<cron>
-
<!--触发器名字,随便取-->
-
<name>Trigger1</name>
-
<!--组名-->
-
<group>group1</group>
-
<!--描述-->
-
<description>trigger</description>
-
<!--作业名,触发哪个作业-->
-
<job-name>Job1</job-name>
-
<!--作业的组名,与作业对应-->
-
<job-group>group1</job-group>
-
<misfire-instruction>DoNothing</misfire-instruction>
-
<cron-expression>0/10 * * * * ?</cron-expression>
-
</cron>
-
</trigger>
-
<job>
-
<!--作业名字,随便取-->
-
<name>Job2</name>
-
<!--组名,随便取-->
-
<group>group2</group>
-
<!--描述-->
-
<description>job2</description>
-
<!--作业类,要定位到我们代码中创建的作业,命名空间.类名-->
-
<job-type>QuartzTest.Job2, QuartzTest</job-type>
-
<durable>true</durable>
-
<recover>false</recover>
-
</job>
-
<trigger>
-
<!--cron表达式触发器-->
-
<cron>
-
<!--触发器名字,随便取-->
-
<name>Trigger2</name>
-
<!--组名-->
-
<group>group2</group>
-
<!--描述-->
-
<description>trigger</description>
-
<!--作业名,触发哪个作业-->
-
<job-name>Job2</job-name>
-
<!--作业的组名,与作业对应-->
-
<job-group>group2</job-group>
-
<misfire-instruction>DoNothing</misfire-instruction>
-
<cron-expression>0/10 * * * * ?</cron-expression>
-
</cron>
-
</trigger>
-
</schedule>
-
</job-scheduling-data>
接着需要配置程序的配置文件,即App.config
-
<?xml version="1.0" encoding="utf-8" ?>
-
<configuration>
-
<configSections>
-
<section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
-
</configSections>
-
<quartz>
-
<add key="quartz.scheduler.instanceName" value="ServerScheduler" />
-
<add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
-
<add key="quartz.threadPool.threadCount" value="10" />
-
<add key="quartz.threadPool.threadPriority" value="2" />
-
<add key="quartz.plugin.xml.type" value = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz" />
-
<add key="quartz.plugin.xml.fileNames" value = "quartz_jobs.xml" />
-
<add key="quartz.jobStore.misfireThreshold" value="60000" />
-
<add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz" />
-
</quartz>
-
</configuration>
这里为了方便,我建立了一个QuartzManage类,里面放了我们需要用到的一些属性:
-
public class QuartzManage
-
{
-
public static string jobName = "";
-
public static string jobGroupName = "";
-
public static string triggerName = "";
-
public static string triggerGroupName = "";
-
//创建一个调度工厂
-
public static IScheduler scheduler = GetScheduler();
-
private static IScheduler GetScheduler()
-
{
-
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
-
IScheduler scheduler = stdSchedulerFactory.GetScheduler();
-
return scheduler;
-
}
-
}
界面设置如下:
这里界面我加入了单例模式,待会解释为什么使用单例模式,使用单例,记得在项目的Program.cs中将Application.Run(New MainView())修改为Application.Run(MainView.GetInstance()):
-
private static MainView mainView = new MainView();
-
public static MainView GetInstance()
-
{
-
return mainView;
-
}
在启动了项目后,应该要启动这些定时任务,并且要将这些任务的信息给显示出来,因此在主窗体加载的时候,需要开启任务,并且将任务信息放入JobDataGridView中:
-
private void MainView_Load(object sender, EvventArgs e)
-
{
-
QuartzManage.scheduler.Start();
-
JobList();
-
}
这里的JobList()是我单独封装的将信息显示在JobDataGridView中的方法:
-
public void JobList()
-
{
-
//此方法获取所有的Job的对象
-
IList<IJobExecutionContext> jobs = QuartzManage.scheduler.GetCurrentlyExecutingJobs();
-
//此方法获取所有的Job的group的名字
-
IList<string> name = QuartzManage.scheduler.GetJobGroupNames();
-
for(int i = 0;i < name.Count;i++)
-
{
-
//给jobDataGridView的行赋值,表明多少行
-
this.jobDataGridView.RowCount = name.Count;
-
//GetJobKeys获取到所有Job的JobKey,相当于Job信息
-
foreach(JobKey jobKey in QuartzManage.scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(name[i])))
-
{
-
this.jobDataGridView.Rows[i].Cells[0].Value = jobKey.Name;
-
this.jobDataGridView.Rows[i].Cells[1].Value = jobKey.Group;
-
}
-
//GetTriggerKeys获取到所有Trigger的TriggerKey,相当于Trigger信息
-
foreach(TriggerKey triggerKey in QuartzManage.scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals(name[i])))
-
{
-
//获取触发器的状态
-
this.jobDataGridView.Rows[i].Cells[2].Value = QuartzManage.shceduler.GetTriggerState(triggerKey);
-
this.jobDataGridView.Rows[i].Cells[3].Value = triggerKey.Name;
-
this.jobDataGridView.Rows[i].Cells[4].Value = trigger.Group;
-
//获取下一次触发时间
-
ICronTrigger cronTrigger = (ICronTrigger)QuartzManage.scheduler.GetTrigger(triggerKey);
-
CronExpression cronExpression = new CronExpression(cronTrigger.CronExpressionString);
-
this.jobDataGridView.Rows[i].Cells[5].Value = cronExpression.GetNextValidTimeAfter(DateTime.Now).Value.ToLocalTime();
-
}
-
}
-
}
接着就是选中jobDataGridView中的一个任务,这里为了让点击一个单元格就选中整行,添加了一些代码,这里不做细讲,可以去查看我的博客中的第19个例子:https://blog.****.net/qq_41061437/article/details/99940094
给jobDataGridView添加CellClick事件:
-
private void JobDataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
-
{
-
QuartzManage.jobName = (string)jobDataGridView.SelectedRows[0].Cells["Jobs"].Value;
-
QuartzManage.jobGroupName = (string)jobDataGridView.SelectedRows[0].Cells["JobGroups"].Value;
-
QuartzManage.triggerName = (string)jobDataGridView.SelectedRows[0].Cells["Triggers"].Value;
-
QuartzManage.triggerGroupName = (string)jobDataGridView.SelectedRows[0].Cells["TriggerGroupName"].Value;
-
}
接着就是选中之后点击暂停按钮,让任务暂停了:
-
private void StopButton_Click(object sender, EventArgs e)
-
{
-
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
-
if(QuartzManage.jobName.Equals(""))
-
{
-
MessageBox.Show("请选择要暂停的任务!");
-
}
-
//判断任务状态
-
else if(QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Paused")
-
{
-
MessageBox.Show("该任务已暂停");
-
}
-
else
-
{
-
QuartzManage.scheduler.PauseTrigger(triggerKey);
-
JobList();
-
MessageBox.Show("任务" + QuartzManage.jobName + "已暂停!");
-
}
-
}
接着就是恢复任务:
-
private void ResumeButton_Click(object sender, EventArgs e)
-
{
-
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
-
if(QuartzManage.jobName.Equals(""))
-
{
-
MessageBox.Show("请先选择要恢复的任务!");
-
}
-
else if(QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Normal")
-
{
-
MessageBox.Show("任务正在运行,无需恢复");
-
}
-
else
-
{
-
QuartzManage.scheduler.ResumeTrigger(triggerKey);
-
JobList();
-
MessageBox.Show("任务" + QuartzManage.jobName + "已恢复!");
-
}
-
}
整个MainView.cs类整合就是:
-
public partial class MainView : Form
-
{
-
private static MainView mainView = new MainView();
-
public static MainView GetInstance()
-
{
-
return mainView;
-
}
-
public MainView()
-
{
-
InitializeComponent();
-
}
-
/// <summary>
-
/// 窗体加载事件
-
/// <summary>
-
private void MainView_Load(object sender, EventArgs e)
-
{
-
QuartzManage.scheduler.Start();
-
JobList();
-
}
-
/// <summary>
-
/// table展示
-
/// <summary>
-
public void JobList()
-
{
-
//此方法获取所有的Job的对象
-
IList<IJobExecutionContext> jobs = QuartzManage.scheduler.GetCurrentlyExecutingJobs();
-
//此方法获取所有的Job的group的名字
-
IList<string> name = QuartzManage.scheduler.GetJobGroupNames();
-
for (int i = 0; i < name.Count; i++)
-
{
-
//给jobDataGridView的行赋值,表明多少行
-
this.jobDataGridView.RowCount = name.Count;
-
//GetJobKeys获取到所有Job的JobKey,相当于Job信息
-
foreach (JobKey jobKey in QuartzManage.scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(name[i])))
-
{
-
this.jobDataGridView.Rows[i].Cells[0].Value = jobKey.Name;
-
this.jobDataGridView.Rows[i].Cells[1].Value = jobKey.Group;
-
}
-
//GetTriggerKeys获取到所有Trigger的TriggerKey,相当于Trigger信息
-
foreach (TriggerKey triggerKey in QuartzManage.scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals(name[i])))
-
{
-
//获取触发器的状态
-
this.jobDataGridView.Rows[i].Cells[2].Value = QuartzManage.scheduler.GetTriggerState(triggerKey);
-
this.jobDataGridView.Rows[i].Cells[3].Value = triggerKey.Name;
-
this.jobDataGridView.Rows[i].Cells[4].Value = triggerKey.Group;
-
//获取下一次触发时间
-
ICronTrigger cronTrigger = (ICronTrigger)QuartzManage.scheduler.GetTrigger(triggerKey);
-
CronExpression cronExpression = new CronExpression(cronTrigger.CronExpressionString);
-
this.jobDataGridView.Rows[i].Cells[5].Value = cronExpression.GetNextValidTimeAfter(DateTime.Now).Value.ToLocalTime();
-
}
-
}
-
}
-
/// <summary>
-
/// 单击CellClick事件
-
/// <summary>
-
private void JobDataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
-
{
-
QuartzManage.jobName = (string)jobDataGridView.SelectedRows[0].Cells["Jobs"].Value;
-
QuartzManage.jobGroupName = (string)jobDataGridView.SelectedRows[0].Cells["JobGroups"].Value;
-
QuartzManage.triggerName = (string)jobDataGridView.SelectedRows[0].Cells["Triggers"].Value;
-
QuartzManage.triggerGroupName = (string)jobDataGridView.SelectedRows[0].Cells["TriggerGroupName"].Value;
-
}
-
/// <summary>
-
/// 暂停任务
-
/// <summary>
-
private void StopButton_Click(object sender, EventArgs e)
-
{
-
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
-
if (QuartzManage.jobName.Equals(""))
-
{
-
MessageBox.Show("请选择要暂停的任务!");
-
}
-
//判断任务状态
-
else if (QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Paused")
-
{
-
MessageBox.Show("该任务已暂停");
-
}
-
else
-
{
-
QuartzManage.scheduler.PauseTrigger(triggerKey);
-
JobList();
-
MessageBox.Show("任务" + QuartzManage.jobName + "已暂停!");
-
}
-
}
-
/// <summary>
-
/// 恢复任务
-
/// <summary>
-
private void ResumeButton_Click(object sender, EventArgs e)
-
{
-
TriggerKey triggerKey = new TriggerKey(QuartzManage.triggerName, QuartzManage.triggerGroupName);
-
if (QuartzManage.jobName.Equals(""))
-
{
-
MessageBox.Show("请先选择要恢复的任务!");
-
}
-
else if (QuartzManage.scheduler.GetTriggerState(triggerKey).ToString() == "Normal")
-
{
-
MessageBox.Show("任务正在运行,无需恢复");
-
}
-
else
-
{
-
QuartzManage.scheduler.ResumeTrigger(triggerKey);
-
JobList();
-
MessageBox.Show("任务" + QuartzManage.jobName + "已恢复!");
-
}
-
}
-
private void MainView_FormClosing(object sender, FormClosingEventArgs e)
-
{
-
DialogResult result = MessageBox.Show("是否关闭窗口?", "关闭", MessageBoxButtons.YesNo, MessageBoxIcon.None);
-
if (result == DialogResult.No)
-
{
-
e.Cancel = true;
-
}
-
else
-
{
-
e.Cancel = false;
-
QuartzManage.scheduler.Shutdown();
-
}
-
}
-
}
启动项目,效果如图:
下面我们来解释一下其中有些地方,比如Job类中,我使用了MainView mainView = MainView.GetInstance();mainView.JobList();这个的作用是为了更新显示的列表,主要是更新下次触发时间,当Job触发之后,那么我们应该修改其的下次触发时间,这里正常来说用个观察者模式来进行更适合一点,但我这里这样用比较简单,也比较方便,因此这样使用。这样应该就能够理解为什么要使用单例模式了,因为我要在其它类里面调用MainView里面的方法,而我只能给一个窗口显示,因此使用的单例模式。
这里触发器都是每10秒触发一次,然后在Job1中,休眠了15秒。也就是说Job1至少需要运行15秒,为了能达到上一个任务运行完再执行下一个任务的效果,这里给Job1加入了[DisallowConcurrentExecutionAttribute]的属性。
当一个任务因为暂停错过触发时间,当再次恢复任务的时候,我们这里直接忽略它,等待下一次触发时间,为了达到此效果,在配置文件中对misfire属性进行了配置:<misfire-instruction>DoNothing</misfire-instruction>,关于其的配置,我们在其的xsd文件中能够清晰的看到几种不同的触发其含有的不同触发机制。
当然,在Quartz中还有很多不错的方法,具体的可以直接点击进源码中进行查看,之后又机会,我会对里面的方法及其使用再进行深入或者简单的介绍。
我已经把这个小demo上传到****,资源下载链接:https://download.****.net/download/qq_41061437/11785180