(精华)2020年8月14日 C#基础知识点 QuartZ任务调度的使用
(精华)2020年8月14日 C#基础知识点 QuartZ任务调度的使用
第一部分:基础的使用
- Nuget引入程序包 QuartZ1. 三大核心对象 IScheduler:时间轴 单元 盒子 在这里进行任务配置 IJobDetail:描述具体做什么事情,定时任务执行的动作 context.MergedJobDataMap 会去掉重复 以后者为准 获取参数严格区分大小写 链式传参: ## 基础代码如下
1:执行代码
#region Scheduler
StdSchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = await factory.GetScheduler();
await scheduler.Start();
#endregion
#region JobDetail
IJobDetail jobDetail = JobBuilder.Create<SendMessage>()//SendMessage是个类
.WithIdentity("sendJob", "group1")
.WithDescription("This is sendJob")
.Build();
jobDetail.JobDataMap.Add("Student1", "参数一");
jobDetail.JobDataMap.Add("Student2", "参数二");
jobDetail.JobDataMap.Add("Student3", "参数三");
jobDetail.JobDataMap.Add("Student4", "参数四");
jobDetail.JobDataMap.Add("Year", DateTime.Now.Year);
#endregion
#region trigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("sendTrigger", "group1")
.StartNow()
.WithCronSchedule("* * * * * ?")//cron表达式
.WithDescription("This is sendJob's sendTrigger")
.Build();
trigger.JobDataMap.Add("Student5", "时间参数5");
trigger.JobDataMap.Add("Student6", "时间参数6");
trigger.JobDataMap.Add("Student7", "时间参数7");
trigger.JobDataMap.Add("Student8", "时间参数8");
trigger.JobDataMap.Add("Year", DateTime.Now.Year + 1);
#endregion
await scheduler.ScheduleJob(jobDetail, trigger);//加入任务和时间策略
2:任务代码
[PersistJobDataAfterExecution] //执行后可以保留执行结果
[DisallowConcurrentExecution] // 保证不去重复执行 可以把任务串行起来 让一个任务执行完毕以后 才去执行下一个任务
public class SendMessage : IJob//必须继承IJob
{<!-- -->
//private static object obj = new object(); //定义一个静态变量也可以实现 执行后可以保留执行结果
public SendMessage()
{<!-- -->
Console.WriteLine("SendMessage 被构造");
}
public async Task Execute(IJobExecutionContext context) //context 很强大 他会包含我们想要的切
{<!-- -->
await Task.Run(() =>
{<!-- -->
//发消息:给谁发,需要传递参数;
Console.WriteLine();
Console.WriteLine("**********************************************");
JobDataMap jobDetailMap = context.JobDetail.JobDataMap;
Console.WriteLine($"{jobDetailMap.Get("Student1")}同学:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{jobDetailMap.Get("Student2")}同学:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{jobDetailMap.Get("Student3")}同学:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{jobDetailMap.Get("Student4")}同学:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{jobDetailMap.Get("Year")}");
jobDetailMap.Put("Year", jobDetailMap.GetInt("Year") + 1);
Console.WriteLine($"{jobDetailMap.Get("Student4")}同学:准备开始上课了!{DateTime.Now}");
JobDataMap triggerMap = context.Trigger.JobDataMap;
Console.WriteLine();
Console.WriteLine($"{triggerMap.Get("Student5")}同学:第二次提示:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{triggerMap.Get("Student6")}同学:第二次提示:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{triggerMap.Get("Student7")}同学:第二次提示:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{triggerMap.Get("Student8")}同学:第二次提示:准备开始上课了!{DateTime.Now}");
Console.WriteLine($"{triggerMap.Get("Year")}");
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
Console.WriteLine(context.MergedJobDataMap.Get("Year"));
Console.WriteLine("**********************************************");
Console.WriteLine();
});
}
}
第二部分:框架日志和监听
执行代码
public static async Task Init()
{<!-- -->
#region 获取框架日志
LogProvider.SetCurrentLogProvider(new CustomConsoleLogProvider());//
#endregion
#region Scheduler
StdSchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = await factory.GetScheduler();
await scheduler.Start();
#region Listener
scheduler.ListenerManager.AddJobListener(new CustomJobListener());
scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener());
scheduler.ListenerManager.AddSchedulerListener(new CustomSchedulerListener());
#endregion
#endregion
#region JobDetail
IJobDetail jobDetail = JobBuilder.Create<SendMessage>()
.WithIdentity("sendJob", "group1")
.WithDescription("This is sendJob")
.Build();
jobDetail.JobDataMap.Add("Student1", "阳光下的微笑");
jobDetail.JobDataMap.Add("Student2", "明日梦");
jobDetail.JobDataMap.Add("Student3", "大白");
jobDetail.JobDataMap.Add("Student4", "池鱼");
jobDetail.JobDataMap.Add("Year", DateTime.Now.Year);
#endregion
#region trigger
//ITrigger trigger = TriggerBuilder.Create()
// .WithIdentity("sendTrigger", "group1")
// //.StartAt(new DateTimeOffset(DateTime.Now.AddSeconds(10)))
// .StartNow()
// .WithCronSchedule("5/3 * * * * ?")//每隔一分钟
// //5,8,11,14
// .WithDescription("This is sendJob's sendTrigger")
// .Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("sendTrigger", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(10) //多少秒执行一次
.WithRepeatCount(10) //表示最多执行多少次
.RepeatForever())
.WithDescription("This is testjob's Trigger")
.Build();
trigger.JobDataMap.Add("Student5", "非常Nice");
trigger.JobDataMap.Add("Student6", "Jerry");
trigger.JobDataMap.Add("Student7", "龙");
trigger.JobDataMap.Add("Student8", "月光寒");
trigger.JobDataMap.Add("Year", DateTime.Now.Year + 1);
#endregion
await scheduler.ScheduleJob(jobDetail, trigger);
}
}
监听Job,Trigger,Scheduler
public class CustomJobListener : IJobListener
{<!-- -->
public string Name => "CustomJobListener";
public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
//便于我们自己添加自己的业务逻辑
Console.WriteLine($"{DateTime.Now} this is JobExecutionVetoed");
});
}
public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine($"{DateTime.Now} this is JobToBeExecuted");
});
}
public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine($"{DateTime.Now} this is JobWasExecuted");
});
}
}
public class CustomSchedulerListener : ISchedulerListener
{<!-- -->
public async Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine($"{jobDetail.Description} 被加入到Scheduler");
});
}
public async Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine($"{jobKey} 被删除 ");
});
}
public Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public async Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine("this is JobScheduled");
});
}
public Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task SchedulerInStandbyMode(CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task SchedulerShutdown(CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task SchedulerShuttingdown(CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public async Task SchedulerStarted(CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine($"this is SchedulerStarted");
});
}
public async Task SchedulerStarting(CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine($"this is SchedulerStarting");
});
}
public Task SchedulingDataCleared(CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
public Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default)
{<!-- -->
throw new NotImplementedException();
}
}
public class CustomTriggerListener : ITriggerListener
{<!-- -->
public string Name => "CustomTriggerListener";
public async Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine("this is TriggerComplete");
});
}
public async Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine("this is TriggerFired");
});
}
public async Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine("this is TriggerComplete");
});
}
public async Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{<!-- -->
await Task.Run(() =>
{<!-- -->
Console.WriteLine("VetoJobExecution");
});
return false; //返回false才能继续执行
}
}
框架日志
public class CustomConsoleLogProvider : ILogProvider
{<!-- -->
public Logger GetLogger(string name) ///可以获取到框架内部的日志信息
{<!-- -->
return new Logger((level, func, exception, parameters) =>
{<!-- -->
if (level >= LogLevel.Info && func != null)
{<!-- -->
Console.WriteLine($"[{ DateTime.Now.ToLongTimeString()}] [{ level}] { func()} {string.Join(";", parameters.Select(p => p == null ? " " : p.ToString()))} 自定义日志{name}");
}
return true;
});
}
public IDisposable OpenNestedContext(string message)
{<!-- -->
throw new NotImplementedException();
}
public IDisposable OpenMappedContext(string key, string value)
{<!-- -->
throw new NotImplementedException();
}
}
第三部分:实现管理可视化
1.新建一个项目 3.访问:http://localhost:50611/CrystalQuartzPanel.axd
4.指定StdSchedulerFactory监控参数 端口和可视化项目的 webconfig 下的provider value 端口保持一直
原有项目代码
#region Scheduler
//StdSchedulerFactory factory = new StdSchedulerFactory();
//IScheduler scheduler = await factory.GetScheduler();
IScheduler scheduler = await ScheduleManager.BuildScheduler();
await scheduler.Start();
#endregion
ScheduleManager类
public class ScheduleManager
{<!-- -->
public async static Task<IScheduler> BuildScheduler()
{<!-- -->
var properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "后台作业监控系统";
// 设置线程池
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "5";
properties["quartz.threadPool.threadPriority"] = "Normal";
// 远程输出配置
properties["quartz.scheduler.exporter.type"] = "Quartz.Simpl.RemotingSchedulerExporter, Quartz";
properties["quartz.scheduler.exporter.port"] = "8008";//端口必须和网站新项目端口一致
properties["quartz.scheduler.exporter.bindName"] = "QuartzScheduler";
properties["quartz.scheduler.exporter.channelType"] = "tcp";
var schedulerFactory = new StdSchedulerFactory(properties);
IScheduler _scheduler = await schedulerFactory.GetScheduler();
return _scheduler;
}
}
第四部分:配置添加任务文件使用
主要改写的代码
//IJobDetail sayHijobDetail = JobBuilder.Create<SayHIJob>()
// .WithDescription("this is sayHijobDetail")
//.WithIdentity("SayhiJob", "Vip高级班")
//.WithDescription("This is SayhiJob")
//.Build();
//ITrigger SayhiTrigger = TriggerBuilder.Create()
// .WithIdentity("SayhiTrigger", "Vip高级班")
// //.StartAt(new DateTimeOffset(DateTime.Now.AddSeconds(10)))
// .StartNow()
// .WithCronSchedule("1/4 * * * * ?")
// .WithDescription("This is SayhiJob's SayhiTrigger")
// .Build();
await scheduler.ScheduleJob(sayHijobDetail, SayhiTrigger);
//使用配置文件
XMLSchedulingDataProcessor processor = new XMLSchedulingDataProcessor(new SimpleTypeLoadHelper());
await processor.ProcessFileAndScheduleJobs("~/CfgFiles/quartz_jobs.xml", scheduler);
配置文件代码
<?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>SayhiJob</name>
<group>Vip高级班</group>
<description>this is sayHijobDetail</description>
<job-type>DispatcherProject.QuartzNet.CustomJob.SayHIJob,DispatcherProject.QuartzNet</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<cron>
<name>SayhiJobTrigger</name>
<group>Vip高级班</group>
<job-name>SayhiJob</job-name>
<job-group>Vip高级班</job-group>
<cron-expression>0/3 * * * * ?</cron-expression>
</cron>
</trigger>
<!--<job>
<name>UpdateInventoryJob</name>
<group>Update</group>
<description>定时更新商品库存</description>
<job-type>TopshelfAndQuartz.UpdateInventoryJob,TopshelfAndQuartz</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<cron>
<name>UpdateInventoryTrigger</name>
<group>Update</group>
<job-name>UpdateInventoryJob</job-name>
<job-group>Update</job-group>
<cron-expression>0 0/1 * * * ?</cron-expression>
</cron>
</trigger>-->
</schedule>
</job-scheduling-data>
第五部分:解决应用程序池回收不生效的问题
WindowsService: 2.添加安装程序
3.就可以安装服务(可以通过工具)
public partial class Service1 : ServiceBase
{<!-- -->
Log4NetLogger log4NetLogger = new Log4NetLogger(typeof(Service1));
public Service1()
{<!-- -->
InitializeComponent();
DispatcherManager.Init().GetAwaiter().GetResult();
}
protected override void OnStart(string[] args)
{<!-- -->
log4NetLogger.Info("Service1 is Start");
}
protected override void OnStop()
{<!-- -->
log4NetLogger.Info("Service1 is Stop");
}
}
编译后进行服务安装
管理员身份打开
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil D:\当前程序路径\bin\Debug\MyDispatcherProjectRichardWindowsService.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil /u D:\当前程序路径\bin\Debug\MyDispatcherProjectRichardWindowsService.exe
服务改名称后,需要先原样卸载,再重新编译安装,要不然卸载还蛮麻烦的