框架_Quartz框架+可视化+log4日志
Quartz目录
1.最简单版本Quarz Demo (Quarz就是包含三个角色: 任务调度(Scheduler),具体任务(Job),时间策略(Trigger))
1.1: Cron时间策略工具 https://cron.qqe2.com/
1.2 Quarz的参数传递与获取
1.3 Quarz的参数传递与获取-获取上一次的执行结果作为参数传递(标记特性+使用put方法)
1.4 设置只有上一个任务完成才会执行下一次任务 5s执行一次, 但是执行的任务时长是10s,下一次任务执行就会产生问题
2:监控策略,框架提供了三个角色的各个操作接口监控调用
4.额外监控策略
5.Quarz可视化 可视化原理:定时服务单独一个进程, Web另起一个进程通过协议去监听定时服务
6.配置文件 JOB和Trigger设置可配置
7.程序集成到WindowsService最终应用
8.添加log4net日志
Quarz为什么分为三个角色:单一职责,(角色分拆)更加灵活装配
//更新字段:
//更新A表中的某一个字段
//更新B表中的某一个字段
//跟新C表中的某一个字段
//跟新D表中的某一个字段
//跟新E表中的某一个字段
不合适做法:没更新表的一个字段都去定义一个Job类
合适做法:定义一个job,,表名称+字段名称作为餐宿传递 一个job解决多个问题;去掉重复
//角色分开以后,就可以自由的配置时间,让一个job 在不同的时间点去执行任务(更加灵活一些)
1.最简单版本Quarz Demo
1.1.添加控制台中心,Nuget添加Quarz
using DispatcherProject.CustomJob; using Quartz; using Quartz.Impl; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; /// <summary> /// 调度项目 /// </summary> namespace DispatcherProject { /// <summary> /// 调度管理 /// </summary> public class QuarZManager { /// <summary> /// 初始化 /// </summary> public async static void Init() { #region 容器 //1.Nuget 引入Quarz //2.创建一个Scheduler 任务调度容器(任务调度都是通过Scheduler来的) StdSchedulerFactory factory = new StdSchedulerFactory(); //创建了一个容器 IScheduler scheduler = await factory.GetScheduler(); #endregion #region 任务 //3.定义JOB 具体的任务定义 IJobDetail job = JobBuilder.Create<SendMeassagerJob>().Build(); #endregion #region 时间 //4.多少时间,执行多少次 //ITrigger trigger = TriggerBuilder.Create() // .WithSimpleSchedule(build=> { // build.WithIntervalInHours(3)//设置时间间隔,时分秒 // .WithMisfireHandlingInstructionFireNow() // .WithRepeatCount(3);//设置执行次数,总共执行3次 //注释掉了就一直执行 // }) // .Build(); ITrigger trigger = TriggerBuilder.Create() .WithSimpleSchedule(build => { build.WithIntervalInSeconds(1)//设置时间间隔,时OR分OR秒 .WithMisfireHandlingInstructionFireNow() .WithRepeatCount(0);//设置执行次数,总共执行N+1次 初始化的时候本身会执行一次 }) .Build(); //5.把时间策略和具体job加入到容器,并设置开始 await scheduler.ScheduleJob( job, trigger); await scheduler.Start(); #endregion } } }
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DispatcherProject.CustomJob { /// <summary> /// 发送信息的具体定时任务 /// </summary> public class SendMeassagerJob : IJob { /// <summary> /// 实现Ijob必须实现的执行方法, /// 在这里定义要做的事情 /// </summary> /// <param name="context"></param> /// <returns></returns> public Task Execute(IJobExecutionContext context) { return Task.Run(() => { Console.WriteLine($"ThreadID={Thread.CurrentThread.ManagedThreadId.ToString("000")}"); Console.WriteLine($"发送一个消息、、初始化自己本身会执行一条"); }); } } }
1.1: Cron时间策略工具 https://cron.qqe2.com/
1.2 Quarz的参数传递与获取
定时任务 获取传递的参数
1.3 Quarz的参数传递与获取-获取上一次的执行结果作为参数传递(标记特性+使用put方法)
1.4 设置只有上一个任务完成才会执行下一次任务 5s执行一次, 但是执行的任务时长是10s,下一次任务执行就会产生问题
2:监控策略,框架提供了三个角色的各个操作接口监控调用
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ZhaoxiFramework.DispatcherProject.CustomListener { public class CustomSchedulerListener : ISchedulerListener { public async Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default) { await Task.Run(() => { Console.WriteLine("CustomSchedulerListener.JobAdd"); }); } public Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } 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 Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } 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 Task SchedulerStarted(CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public Task SchedulerStarting(CancellationToken cancellationToken = default) { throw new NotImplementedException(); } 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(); } } }
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ZhaoxiFramework.DispatcherProject.CustomListener { public class CustomJobListener : IJobListener { public string Name => "CustomJobListener"; public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default) { await Task.Run(() => { Console.WriteLine("CustomJobListener.JobExecutionVetoed"); }); } public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default) { await Task.Run(() => { Console.WriteLine("CustomJobListener.JobToBeExecuted"); }); } public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default) { await Task.Run(()=> { Console.WriteLine("CustomJobListener.JobWasExecuted"); }); } } }
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ZhaoxiFramework.DispatcherProject.CustomListener { public class CustomTriggerListener : ITriggerListener { public string Name => "CustomTriggerListener"; /// <summary> /// 完成时执行 /// </summary> /// <param name="trigger"></param> /// <param name="context"></param> /// <param name="triggerInstructionCode"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default) { await Task.Run(()=> { Console.WriteLine("CustomTriggerListener.TriggerComplete"); }); } /// <summary> /// /// </summary> /// <param name="trigger"></param> /// <param name="context"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default) { await Task.Run(() => { Console.WriteLine($"CustomTriggerListener TriggerFired {trigger.Description}"); }); } public async Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default) { await Task.Run(() => { Console.WriteLine($"CustomTriggerListener TriggerMisfired {trigger.Description}"); }); } /// <summary> /// 要不要放弃job /// </summary> /// <param name="trigger"></param> /// <param name="context"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default) { await Task.Run(() => { Console.WriteLine($"CustomTriggerListener VetoJobExecution {trigger.Description}"); }); return false; } } }
3.具体执行策略中心
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DispatcherProject.QuartzNet.CustomJob { [PersistJobDataAfterExecution] //锚点1:设置这个后 可以在执行后可以保留执行结果 [DisallowConcurrentExecution] //设置这个后 让定时执行的任务变成串行,必须等待前面任务执行完成才会继续执行下一个任务,就像设置每5秒执行一次任务,但是前面任务没执行完成,后面任务又开始了就会存在问题 public class SendMessage : IJob { //private static object obj = new object(); //定义一个静态变量也可以实现 执行后可以保留执行结果 public SendMessage() { Console.WriteLine("SendMessage 被构造"); } public async Task Execute(IJobExecutionContext context) //context 很强大 他会包含我们想要的切 { await Task.Run(() => { Thread.Sleep(5000); #region sendJob //发消息:给谁发,需要传递参数; Console.WriteLine(); Console.WriteLine("**********************************************"); JobDataMap jobDetailMap = context.JobDetail.JobDataMap; Console.WriteLine($"{jobDetailMap.Get("字符串1")}{DateTime.Now}"); Console.WriteLine($"{jobDetailMap.Get("字符串2")}{DateTime.Now}"); Console.WriteLine($"{jobDetailMap.Get("字符串3")}{DateTime.Now}"); Console.WriteLine($"{jobDetailMap.Get("字符串4")}{DateTime.Now}"); Console.WriteLine($"{jobDetailMap.Get("Year")}"); jobDetailMap.Put("Year", jobDetailMap.GetInt("Year") + 1); JobDataMap triggerMap = context.Trigger.JobDataMap; Console.WriteLine(); #endregion #region trigger Console.WriteLine($"{triggerMap.Get("时间策略的字符串1")}{DateTime.Now}"); Console.WriteLine($"{triggerMap.Get("时间策略的字符串2")}{DateTime.Now}"); Console.WriteLine($"{triggerMap.Get("时间策略的字符串3")}{DateTime.Now}"); Console.WriteLine($"{triggerMap.Get("时间策略的字符串4")}{DateTime.Now}"); //锚点1 获取执行的结果 var getYear = triggerMap.Get("Year"); Console.WriteLine($"{triggerMap.Get("Year")}"); Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); Console.WriteLine(context.MergedJobDataMap.Get("Year")); Console.WriteLine("**********************************************"); Console.WriteLine(); #endregion }); } } }
4.额外监控策略
1.自定义作业监听器 监控工作任务
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DispatcherProject.QuartzNet.CustomListener { /// <summary> /// 自定义作业监听器,需要实行接口IJobListener /// </summary> 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"); }); } } }
2.自定义调度程序监听器 监控调度程序
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DispatcherProject.QuartzNet.CustomListener { /// <summary> /// 自定义调度程序侦听器 /// </summary> 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(); } } }
3.自定义触发监听器 监控时间策略
using Quartz; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DispatcherProject.QuartzNet.CustomListener { /// <summary> /// 自定义触发监听器 /// </summary> 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才能继续执行 } } }
5.Quarz可视化 可视化原理:定时服务单独一个进程, Web另起一个进程通过协议去监听定时服务
5.1:新建Web MVC程序添加Quarz+CrystalQuarz.Remote引用,注意依赖项 否则会报错
5.2 添加类
using Quartz; using Quartz.Impl; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DispatcherProject { 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"] = "5005"; properties["quartz.scheduler.exporter.bindName"] = "QuartzScheduler"; properties["quartz.scheduler.exporter.channelType"] = "tcp"; var schedulerFactory = new StdSchedulerFactory(properties); IScheduler _scheduler = await schedulerFactory.GetScheduler(); return _scheduler; } } }
5.3 设置web端 web.conig的SchedulerHost 与 Quarz定时程序中 ScheduleManager设置的属性相同
5.4 Quatz容器使用ScheduleManager
5.5访问可视化界面
web程序下面的web.config path就是可视化界面的路径
English不好可以在谷歌浏览器右键选择翻译成中文
6.配置文件 配置任务
1.设置始终复制
2.设置读取配置文件
7.程序集成到WindowsService最终应用
7.1因为vs项目都是寄宿在IIS上 30分钟内没有任何请求 网站就会回收 所以就不符合定时任务
7.2新建windos服务之后 点击这个 右键安装程序
添加之后重新生成 打开所在的文件夹找到exe文件
7.3使用cmd命令 就可以执行安装windos服务了
请注意安装失败 要用:管理员身份打开
安装
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil C:\Users\Administrator.WIN-5SSJ2G8Q8GU\source\repos\QuarZWindosService\bin\Debug\QuarZWindosService.exe
卸载
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil /u C:\Users\Administrator.WIN-5SSJ2G8Q8GU\source\repos\QuarZWindosService\bin\Debug\QuarZWindosService.exe
服务改名称后,需要先原样卸载,再重新编译安装,要不然卸载还蛮麻烦的
8.添加log4net日志记录
1:管理Nuget包 添加log4net引用
2:添加log类
using log4net; using log4net.Config; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Common { public class CustomLogger { static CustomLogger() { XmlConfigurator.Configure(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CfgFiles\\log4net.cfg.xml"))); ILog Log = LogManager.GetLogger(typeof(CustomLogger)); Log.Info("系统初始化Logger模块"); } private ILog loger = null; public CustomLogger(Type type) { loger = LogManager.GetLogger(type); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> /// <param name="ex"></param> public void Error(string msg = "出现异常", Exception ex = null) { Console.WriteLine(msg); loger.Error(msg, ex); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Warn(string msg) { Console.WriteLine(msg); loger.Warn(msg); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Info(string msg) { Console.WriteLine(msg); loger.Info(msg); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Debug(string msg) { Console.WriteLine(msg); loger.Debug(msg); } } }
3.添加配置文件 记得要设置始终复制
<?xml version="1.0" encoding="utf-8"?> <log4net> <!-- Define some output appenders --> <appender name="rollingAppender" type="log4net.Appender.RollingFileAppender"> <file value="log\log.txt" /> <!--追加日志内容--> <appendToFile value="true" /> <!--防止多线程时不能写Log,官方说线程非安全--> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <!--可以为:Once|Size|Date|Composite--> <!--Composite为Size和Date的组合--> <rollingStyle value="Composite" /> <!--当备份文件时,为文件名加的后缀--> <datePattern value="yyyyMMdd.TXT" /> <!--日志最大个数,都是最新的--> <!--rollingStyle节点为Size时,只能有value个日志--> <!--rollingStyle节点为Composite时,每天有value个日志--> <maxSizeRollBackups value="20" /> <!--可用的单位:KB|MB|GB--> <maximumFileSize value="3MB" /> <!--置为true,当前最新日志文件名永远为file节中的名字--> <staticLogFileName value="true" /> <!--输出级别在INFO和ERROR之间的日志--> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="INFO" /> <param name="LevelMax" value="FATAL" /> </filter> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/> </layout> </appender> <!-- levels: OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL --> <root> <priority value="ALL"/> <level value="ALL"/> <appender-ref ref="rollingAppender" /> </root> </log4net>
4:使用日志
添加之后Log4net之后:重新生成 在卸载服务,安装服务 然后服务记得启动
日志和正常输出
本文来自博客园,作者:12不懂3,转载请注明原文链接:https://www.cnblogs.com/LZXX/p/13178125.html