.KeyEquals(newJobKey(\"sample1\",\"jobgroup1\")));","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"fVO4-1687162354638","name":"code-line","data":{},"nodes":[{"type":"text","id":"HKrS-1687162354636","leaves":[{"text":"//将作业和从触发器绑定至作业调度上\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"ROHL-1687161964997","name":"code-line","data":{},"nodes":[{"type":"text","id":"V9rL-1687161964996","leaves":[{"text":"awaitscheduler.ScheduleJob(job,trigger);\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"SlgU-1687162375425","name":"code-line","data":{},"nodes":[{"type":"text","id":"hUcU-1687162375423","leaves":[{"text":"//启动作业调度","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"xmGN-1687161964999","name":"code-line","data":{},"nodes":[{"type":"text","id":"RONa-1687161964998","leaves":[{"text":"awaitscheduler.Start();\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"l6Cs-1687161965001","name":"code-line","data":{},"nodes":[{"type":"text","id":"Kmjm-1687161965000","leaves":[{"text":"\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"86Cu-1687161965003","name":"code-line","data":{},"nodes":[{"type":"text","id":"qCcC-1687161965002","leaves":[{"text":"Console.WriteLine(\"--------14\");\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"c6Tl-1687161965005","name":"code-line","data":{},"nodes":[{"type":"text","id":"PCLz-1687161965004","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"RS0S-1687161965007","name":"code-line","data":{},"nodes":[{"type":"text","id":"vsxl-1687161965006","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"Vl4I-1687161965009","name":"code-line","data":{},"nodes":[{"type":"text","id":"c0RA-1687161965008","leaves":[{"text":"//作业侦听器\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"VF9t-1687161965011","name":"code-line","data":{},"nodes":[{"type":"text","id":"e3hb-1687161965010","leaves":[{"text":"publicclassJobListen:JobListenerSupport\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"6pM7-1687161965013","name":"code-line","data":{},"nodes":[{"type":"text","id":"x1YH-1687161965012","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"aEpf-1687161965015","name":"code-line","data":{},"nodes":[{"type":"text","id":"aOG0-1687161965014","leaves":[{"text":"publicJobListen()\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"3cDS-1687161965017","name":"code-line","data":{},"nodes":[{"type":"text","id":"WMmB-1687161965016","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"uiyX-1687161965019","name":"code-line","data":{},"nodes":[{"type":"text","id":"xMEE-1687161965018","leaves":[{"text":"Console.WriteLine(\"--------20\");\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"cxcB-1687161965021","name":"code-line","data":{},"nodes":[{"type":"text","id":"Mnr9-1687161965020","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"WKCS-1687161965023","name":"code-line","data":{},"nodes":[{"type":"text","id":"nuIo-1687161965022","leaves":[{"text":"publicoverridestringName{get{return\"JobListen20\";}}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"SJlh-1687161965025","name":"code-line","data":{},"nodes":[{"type":"text","id":"5LGA-1687161965024","leaves":[{"text":"//调用job之前执行\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"e0sq-1687161965027","name":"code-line","data":{},"nodes":[{"type":"text","id":"azuB-1687161965026","leaves":[{"text":"publicoverrideTaskJobToBeExecuted(IJobExecutionContextcontext,CancellationTokencancellationToken)\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"Nd8X-1687161965029","name":"code-line","data":{},"nodes":[{"type":"text","id":"uADz-1687161965028","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"7qUh-1687161965031","name":"code-line","data":{},"nodes":[{"type":"text","id":"u286-1687161965030","leaves":[{"text":"Console.WriteLine(\"--------21\");\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"MkIC-1687161965033","name":"code-line","data":{},"nodes":[{"type":"text","id":"Wx6M-1687161965032","leaves":[{"text":"returnbase.JobToBeExecuted(context,cancellationToken);\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"0Z1U-1687161965035","name":"code-line","data":{},"nodes":[{"type":"text","id":"EvyH-1687161965034","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"HXi2-1687161965037","name":"code-line","data":{},"nodes":[{"type":"text","id":"1KkY-1687161965036","leaves":[{"text":"}","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"jqj6-1687162448650","name":"code-line","data":{},"nodes":[{"type":"text","id":"Y6jI-1687162448649","leaves":[{"text":"//日志组件\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"Wi6O-1687161965039","name":"code-line","data":{},"nodes":[{"type":"text","id":"ow12-1687161965038","leaves":[{"text":"publicclassConsoleLogProvider:ILogProvider\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"u67z-1687161965041","name":"code-line","data":{},"nodes":[{"type":"text","id":"yVNX-1687161965040","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"rE7p-1687161965043","name":"code-line","data":{},"nodes":[{"type":"text","id":"4qy9-1687161965042","leaves":[{"text":"publicLoggerGetLogger(stringname)\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"ukxy-1687161965045","name":"code-line","data":{},"nodes":[{"type":"text","id":"8xDY-1687161965044","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"5jNp-1687161965047","name":"code-line","data":{},"nodes":[{"type":"text","id":"seNE-1687161965046","leaves":[{"text":"return(level,func,exception,parameters)=>\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"9WQ2-1687161965049","name":"code-line","data":{},"nodes":[{"type":"text","id":"BflN-1687161965048","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"DKrq-1687161965051","name":"code-line","data":{},"nodes":[{"type":"text","id":"DBZG-1687161965050","leaves":[{"text":"if(level>=LogLevel.Info&&func!=null)\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"Tdmv-1687161965053","name":"code-line","data":{},"nodes":[{"type":"text","id":"x8w1-1687161965052","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"kHG6-1687161965055","name":"code-line","data":{},"nodes":[{"type":"text","id":"CJJO-1687161965054","leaves":[{"text":"Console.WriteLine(\"[\"+DateTime.Now.ToLongTimeString()+\"][\"+level+\"]\"+func(),parameters);\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"uDz8-1687161965057","name":"code-line","data":{},"nodes":[{"type":"text","id":"JbUz-1687161965056","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"Gbau-1687161965059","name":"code-line","data":{},"nodes":[{"type":"text","id":"1Jol-1687161965058","leaves":[{"text":"returntrue;\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"b1sP-1687161965061","name":"code-line","data":{},"nodes":[{"type":"text","id":"xjFv-1687161965060","leaves":[{"text":"};\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"MNJf-1687161965063","name":"code-line","data":{},"nodes":[{"type":"text","id":"bHOS-1687161965062","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"gIO2-1687161965065","name":"code-line","data":{},"nodes":[{"type":"text","id":"xvRo-1687161965064","leaves":[{"text":"publicIDisposableOpenNestedContext(stringmessage)\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"A42h-1687161965067","name":"code-line","data":{},"nodes":[{"type":"text","id":"fFF7-1687161965066","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"j6q1-1687161965069","name":"code-line","data":{},"nodes":[{"type":"text","id":"Zwbr-1687161965068","leaves":[{"text":"thrownewNotImplementedException();\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"v57d-1687161965071","name":"code-line","data":{},"nodes":[{"type":"text","id":"a1HK-1687161965070","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"verW-1687161965073","name":"code-line","data":{},"nodes":[{"type":"text","id":"bv48-1687161965072","leaves":[{"text":"publicIDisposableOpenMappedContext(stringkey,objectvalue,booldestructure=false)\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"d4GC-1687161965075","name":"code-line","data":{},"nodes":[{"type":"text","id":"UMta-1687161965074","leaves":[{"text":"{\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"sVnQ-1687161965077","name":"code-line","data":{},"nodes":[{"type":"text","id":"hTY0-1687161965076","leaves":[{"text":"thrownewNotImplementedException();\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"xpkq-1687161965079","name":"code-line","data":{},"nodes":[{"type":"text","id":"PIjd-1687161965078","leaves":[{"text":"}\r","marks":[{"type":"fontSize","value":14}]}]}]},{"type":"block","id":"Bry6-1687161965081","name":"code-line","data":{},"nodes":[{"type":"text","id":"kHU2-1687161965080","leaves":[{"text":"}","marks":[{"type":"fontSize","value":14}]}]}]}],"state":{"language":"typescript"}},{"type":"block","id":"kMKw-1687161963230","name":"heading","data":{"level":"h5","style":{}},"nodes":[{"type":"text","id":"h4Or-1687161963231","leaves":[{"text":"执行结果","marks":[]}]}],"state":{}},{"type":"block","id":"rjvE-1687162003033","name":"paragraph","data":{},"nodes":[{"type":"text","id":"c2KH-1687162003031","leaves":[{"text":"","marks":[{"type":"fontSize","value":14}]}]}],"state":{}},{"type":"block","id":"4k6t-1687162236435","name":"image","data":{"version":1,"url":"https://note.youdao.com/yws/res/7/WEBRESOURCEce1e0087065686034f05d5e3d91acbf7","width":1121,"height":989},"nodes":[],"state":{"loading":false,"renderSource":"https://note.youdao.com/yws/res/7/WEBRESOURCEce1e0087065686034f05d5e3d91acbf7","initialSize":{"width":1121,"height":989}}},{"type":"block","id":"kR6k-1687162236439","name":"paragraph","data":{},"nodes":[{"type":"text","id":"KwHi-1687162236438","leaves":[{"text":"","marks":[{"type":"fontSize","value":14}]}]}],"state":{}}]">Quartz.NET 官网
Quartz.net是什么
Quartz.NET 是一个功能齐全的开源作业调度系统,他的前身来源于java的Quartz.Quartz.net安装和使用
基于visual studio引用安装,其他IDE类似,或者下载DLL手动引用也是可以的;运行环境基于.net core,源.net程序类似Quartz.net的架构和关键属性以及方法
三个主要的概念
- scheduler 作业调度,作业计划在给定触发器发生时运行,实际就是领导
- job 作业,实现简单 IJob 接口的任何 .NET 类,实际就是干活的员工
- trigger 侦听器,负责捕获调度事件以监视或控制作业,实际就是监工
监工发现员工偷懒了,报告给领导,领导知道后,给员工派了很多活,导致了员工天天996. 大概是这么个关系Quartz.net的一些关键属性
类型 |
ISchedulerFactory | SchedulerBuilder的工厂类 |
IScheduler | 用于与调度程序交互的主要 API |
SchedulerBuilder | 用于定义/构建调度程序实例,需要 Quartz 3.1 或更高版本 |
IJobFactory | JobBuilder的工厂类 |
IJob | 由您希望由调度程序执行的组件实现的接口 |
IJobDetail | 用于定义作业的实例 |
JobBuilder | 用于定义/构建 JobDetail 实例,这些实例定义作业的实例 |
TriggerBuilder | 用于定义/构建触发器实例 |
ITrigger | 定义执行给定作业的计划的一个组件,作业可以有多个关联的触发器 |
ListenerManager | 侦听器事件,例如:执行job工作之前,之后触发等等,同时也可用于触发器侦听 |
IServiceCollectionQuartzConfigurator 参数
Scheduler Name | 调度作业的名称 |
Scheduler Id | SchedulerId |
Max Batch Size | 同时执行job的最大数量 |
InterruptJobsOnShutdown |
InterruptJobsOnShutdownWithWait |
BatchTriggerAcquisitionFireAheadTimeWindow |
在通用host或者webhost中的最佳实践
通用host或者webhost代码是一样的
执行流程
- 在通用主机服务中注入服务AddQuartz,AddQuartzHostedService
- 在AddQuartz中配置调度作业的基本属性(SchedulerId等等)和调度器以及作业(ScheduleJob,AddJob,AddTrigger);可以在这个地方写入所有的调度作业,也可以写入一个initjob作业,在主机完全启动5秒后执行相应的业务(可规避掉某些依赖服务未启动的问题)
- 在initjob中,初始化其他定时任务。官网介绍job只能有一个无参的构造函数,但我亲测可以注入(笑脸)
- 关于job和reigger的具体参数,可查看官网
如下
以下代码和执行结果,其中执行顺序一目了然代码
static void Main(string[] args) { Console.WriteLine("Hello, World!"); LogProvider.SetCurrentLogProvider(new ConsoleLogProvider()); //通用主机配置 var build = Host.CreateDefaultBuilder(args) .ConfigureServices((host, services) => { Console.WriteLine("--------1"); //调度作业的唯一id的唯一标识,用于集群搭建cluster q.SchedulerId = "SchedulerId_01"; //配置Quartz服务 services.AddQuartz(q => { Console.WriteLine("--------2"); //依赖注入,ISchedulerFactory,Ijob等等 q.UseMicrosoftDependencyInjectionJobFactory(); //方法一和方法二使用不同方法的写法,本质基本是一样的 //方法一 q.ScheduleJob( trigger => { Console.WriteLine("--------33"); //WithIdentity 绑定触发器或者job的唯一属性和组 //TriggerKey,JobKey 都是代表唯一个属性和组 trigger.WithIdentity(new TriggerKey("trigger1", "triggergroup1")) .WithSimpleSchedule(x => x.WithIntervalInSeconds(5)) // .StartAt(DateBuilder.EvenSecondDate(DateTimeOffset.UtcNow.AddSeconds(5))) // .WithDailyTimeIntervalSchedule(x => x.WithInterval(10, IntervalUnit.Second)) .WithDescription("init 描述"); }, jobConfigure => { Console.WriteLine("--------44"); jobConfigure.WithIdentity(new JobKey("Init1", "jobgroup1")); } ); //方法二 //q.AddJob(opts => //{ // Console.WriteLine("--------3"); // opts.WithIdentity(new JobKey("Init1", "jobgroup1")); //}); //q.AddTrigger(opts => //{ // Console.WriteLine("--------4"); // //将job添加至触发器中 // opts.ForJob(new JobKey("Init1", "jobgroup1")) // .WithIdentity("trigger1", "triggergroup1") // .WithSimpleSchedule(x => // { // Console.WriteLine("--------6"); // x.WithIntervalInSeconds(5); // //.RepeatForever(); // //.WithRepeatCount(5); // }); //}); }); services.AddQuartzHostedService(options => { options.WaitForJobsToComplete = true; }); }).Build(); //var schedulerFactory = build.Services.GetService(); //var scheduler = schedulerFactory.GetScheduler(); build.Run(); Console.WriteLine("--------7"); } } public class SampleJob : IJob { public SampleJob(ISchedulerFactory schedulerFactory, IJobFactory jobFactory) { Console.WriteLine("--------8"); } public async Task Execute(IJobExecutionContext context) { Console.WriteLine("--------9"); context.JobDetail.JobDataMap.GetString("我是sample的job数据key"); Console.WriteLine($"我是sample的job数据key: {context.JobDetail.JobDataMap.GetString("我是sample的job数据key")}"); Console.WriteLine($"我是sample的Trigger数据key: {context.MergedJobDataMap.GetString("我是sample的Trigger数据key")}"); } } public class InitJob : IJob { public ISchedulerFactory _schedulerFactory; public IJobFactory _jobFactory; public InitJob(ISchedulerFactory schedulerFactory, IJobFactory jobFactory) { Console.WriteLine("--------12"); _schedulerFactory = schedulerFactory; _jobFactory = jobFactory; } public async Task Execute(IJobExecutionContext context) { Console.WriteLine("--------13"); Console.WriteLine("InitJob Execute " + Random.Shared.Next(0, 100)); //创建job IJobDetail job = JobBuilder.Create() //写入参数 .UsingJobData("我是sample的job数据key", "我是sample的job数据value") .WithIdentity("sample1", "jobgroup1").Build(); //创建触发器 ITrigger trigger = TriggerBuilder.Create() .UsingJobData("我是sample的Trigger数据key", "我是sample的Trigger数据value") .WithIdentity("trigger_sample1", "triggergroup1") .WithDescription("我是描述") //通过corn符号来创建触发器 //.WithCronSchedule(taskOptions.CronExpression) .WithSimpleSchedule(x => x.WithIntervalInSeconds(5) //5秒后执行 .RepeatForever() //重复 ) .Build(); //通过工厂获取一个作业调度 var scheduler = await _schedulerFactory.GetScheduler(); //绑定一个job的事件侦听器,从执行顺序上看 new JobListen是一个单例类 scheduler.ListenerManager.AddJobListener(new JobListen(), KeyMatcher.KeyEquals(new JobKey("sample1", "jobgroup1"))); //将作业和从触发器绑定至作业调度上 await scheduler.ScheduleJob(job, trigger); //启动作业调度 await scheduler.Start(); Console.WriteLine("--------14"); } } //作业侦听器 public class JobListen : JobListenerSupport { public JobListen() { Console.WriteLine("--------20"); } public override string Name { get { return "JobListen20"; } } //调用job之前执行 public override Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken) { Console.WriteLine("--------21"); return base.JobToBeExecuted(context, cancellationToken); } } //日志组件 public class ConsoleLogProvider : ILogProvider { public Logger GetLogger(string name) { return (level, func, exception, parameters) => { if (level >= LogLevel.Info && func != null) { Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters); } return true; }; } public IDisposable OpenNestedContext(string message) { throw new NotImplementedException(); } public IDisposable OpenMappedContext(string key, object value, bool destructure = false) { throw new NotImplementedException(); } }
执行结果