亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

作業(yè)調(diào)度框架Quartz.net用法詳解

 更新時(shí)間:2022年06月09日 15:33:35   作者:springsnow  
本文詳細(xì)講解了作業(yè)調(diào)度框架Quartz.net的用法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、介紹

Quartz.NET是一個(gè)強(qiáng)大、開源、輕量的作業(yè)調(diào)度框架,你能夠用它來為執(zhí)行一個(gè)作業(yè)而創(chuàng)建簡(jiǎn)單的或復(fù)雜的作業(yè)調(diào)度。它有很多特征,如:數(shù)據(jù)庫(kù)支持,集群,插件,支持cron-like表達(dá)式等等。在2017年的最后一天Quartz.NET 3.0發(fā)布,正式支持了.NET Core 和async/await。這是一個(gè)大版本,有眾多新特性和大的功能

官網(wǎng):http://www.quartz-scheduler.net/

源碼:https://github.com/quartznet/quartznet

示例:https://www.quartz-scheduler.net/documentation/quartz-3.x/quick-start.html

新功能

  • 支持 async/await 基于任務(wù)的作業(yè),內(nèi)部以async/await工作
  • 支持.NET Core / netstandard 2.0和.NET Framework 4.5.2及更高版本
  • 通過提供程序名稱SQLite-Microsoft支持Microsoft.Data.Sqlite,舊的提供程序SQLite也仍然有效,還可以用
  • 增加了對(duì)SQL Server內(nèi)存優(yōu)化表的初步支持和Quartz.Impl.AdoJobStore.UpdateLockRowSemaphoreMOT
  • 從依賴關(guān)系中刪除Common.Logging
  • 刪除C5 Collections,使用.NET框架內(nèi)置的Collections
  • 在插件啟動(dòng)時(shí)添加對(duì)作業(yè)調(diào)度XML文件的驗(yàn)證
  • 在TimeZoneUtil中添加對(duì)額外自定義時(shí)區(qū)解析器功能的支持

Quartz3.x和2.x最大的優(yōu)勢(shì)在于:支持異步執(zhí)行Job,所以建議將Job的Excute方法設(shè)計(jì)為異步方法,這樣做可以提高任務(wù)調(diào)度和執(zhí)行效率。

quartz的構(gòu)成和基本工作流程

在使用Quart.net前,我們先理解下quartz的構(gòu)成和基本工作流程,

1、Quartz包含以下五個(gè)基本部分:

  • Scheduler 調(diào)度器,quartz工作時(shí)的獨(dú)立容器
  • Trigger 觸發(fā)器,定義了調(diào)度任務(wù)的時(shí)間規(guī)則
  • Job 調(diào)度的任務(wù)
  • ThreadPool 線程池(不是clr中的線程池),任務(wù)最終交給線程池中的線程執(zhí)行
  • JobStore RAWStore和DbStore兩種,job和trigger都存放在JobStore中

2、Quartz的基本工作流程

scheduler是quartz的獨(dú)立運(yùn)行容器,trigger和job都可以注冊(cè)在scheduler容器中。

其中trigger是觸發(fā)器,用于定義調(diào)度任務(wù)的時(shí)間規(guī)則,job是被調(diào)度的任務(wù),一個(gè)job可以有多個(gè)觸發(fā)器,而一個(gè)觸發(fā)器只能屬于一個(gè)job。

Quartz中一個(gè)調(diào)度線程QuartzSchedulerThread,調(diào)度線程可以找到將要被觸發(fā)的trigger,通過trigger找到要執(zhí)行的job,然后在ThreadPool中獲取一個(gè)線程來執(zhí)行這個(gè)job。

JobStore主要作用是存放job和trigger的信息。

二、基于文件配置

Quartz.NET 在3.x已經(jīng)將插件分離了,所以如果要從xml直接加載文件,需要引入插件包

1、引入包

2、配置

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="QuartzScheduler"/>
    <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz"/>
    <add key="quartz.threadPool.threadCount" value="10"/>
    ******************************Plugin配置*********************************************
    <add key="quartz.plugin.jobInitializer.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins"/>
    <add key="quartz.plugin.jobInitializer.fileNames" value="quartz_jobs.xml"/>
  </quartz>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
  </startup>

</configuration>

quartz_jobs.xml

<xml version="1.0" encoding="UTF-8"?>

 This file contains job definitions in schema version 2.0 format 

<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>

    開始執(zhí)行一個(gè)調(diào)度
    <job>
      <name>jldwjob</name>
      <group>kelun</group>
      <description>計(jì)量單位</description>
      格式:實(shí)現(xiàn)了IJob接口的包含完整命名空間的類名,程序集名稱
      <durable>true</durable>
      <recover>false</recover>
    </job>
    
    <trigger>
      <cron>
        <name>jlwd</name>
        <group>kelun</group>
        <job-name>jldwjob</job-name>
        <job-group>kelun</job-group>
        2018-01-22T00:00:00+08:00
        每3秒執(zhí)行一次
      </cron>
    </trigger>

  </schedule>
</job-scheduling-data>

3、接口實(shí)現(xiàn)

using Kelun.Log4Net;
using Quartz;
using System.Reflection;
using System.Threading.Tasks;

namespace Kelun
{
    class JldwJob : IJob
    {

        private static readonly IMyLog Logger = MyLogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        public Task Execute(IJobExecutionContext context)
        {
            return Task.Run(() => 
            {
                Logger.Info("定時(shí)任務(wù)執(zhí)行");
            });
        }
    }
}

4、啟動(dòng)Quartz

public MainForm()
{
    InitializeComponent();
    RunProgramAsync().GetAwaiter().GetResult();
}

private static async Task RunProgramAsync()
{
    try
    {
        StdSchedulerFactory factory = new StdSchedulerFactory();

        IScheduler scheduler = await factory.GetScheduler();

        //開啟調(diào)度器
        await scheduler.Start();

        //創(chuàng)建一個(gè)作業(yè)
        //IJobDetail job = JobBuilder.Create().WithIdentity("myJob", "group1").Build();

        //ITrigger trigger = TriggerBuilder.Create()
        //    .WithIdentity("myTrigger", "group1")
        //    .StartNow()     //現(xiàn)在開始
        //    .WithSimpleSchedule(x => x
        //        .WithIntervalInSeconds(1)   //觸發(fā)時(shí)間,1秒一次
        //        .RepeatForever())
        //    .Build();

        //把作業(yè),觸發(fā)器加入調(diào)度器。
       //await scheduler.ScheduleJob(job, trigger);  

        //await scheduler.Shutdown();

    }
    catch (SchedulerException se)
    {
        Logger.Error("執(zhí)行錯(cuò)誤", se);
    }
}

5、禁用Quartz.NET日志輸出

<logger name="Quartz">
    <level value="OFF" />
</logger>

6、當(dāng)應(yīng)用或網(wǎng)站關(guān)閉時(shí)結(jié)束正在執(zhí)行的工作

在Global.asax中的Application_End方法中添加如下代碼:

        protected void Application_End(object sender, EventArgs e)
        {
            //   在應(yīng)用程序關(guān)閉時(shí)運(yùn)行的代碼
            if (scheduler != null)
            {
                 scheduler.Shutdown(true);
             }
         }

三、基于代碼的方式

用五分鐘看一個(gè)簡(jiǎn)單的例子吧,這個(gè)例子中通過調(diào)度器工廠StdSchedulerFactory獲取一個(gè)調(diào)度器實(shí)例scheduler,然后定義Job和trigger,并注冊(cè)到調(diào)度器中,最后啟動(dòng)scheduler就可以執(zhí)行我們的任務(wù)了。代碼如下:

using Quartz;
using Quartz.Impl;
using System;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            //調(diào)度器,生成實(shí)例的時(shí)候線程已經(jīng)開啟了,不過是在等待狀態(tài)
            StdSchedulerFactory factory = new StdSchedulerFactory();
            IScheduler scheduler = factory.GetScheduler().Result;

            //創(chuàng)建一個(gè)Job,綁定MyJob
            IJobDetail job = JobBuilder.Create()              //獲取JobBuilder
                                .WithIdentity("jobname1", "group1")  //添加Job的名字和分組
                                .WithDescription("一個(gè)簡(jiǎn)單的任務(wù)")   //添加描述
                                .Build();                            //生成IJobDetail

            //創(chuàng)建一個(gè)觸發(fā)器
            ITrigger trigger = TriggerBuilder.Create()                   //獲取TriggerBuilder  
                              .StartAt(DateBuilder.TodayAt(01, 00, 00))  //開始時(shí)間,今天的1點(diǎn)(hh,mm,ss),可使用StartNow()
                              .ForJob(job)                               //將觸發(fā)器關(guān)聯(lián)給指定的job
                              .WithPriority(10)                          //優(yōu)先級(jí),當(dāng)觸發(fā)時(shí)間一樣時(shí),優(yōu)先級(jí)大的觸發(fā)器先執(zhí)行
                              .WithIdentity("tname1", "group1")          //添加觸發(fā)器的名字和分組
                              .WithSimpleSchedule(x => x.WithIntervalInSeconds(1) //調(diào)度,一秒執(zhí)行一次,執(zhí)行三次
                                                        .WithRepeatCount(3)
                                                        .Build())
                              .Build();

            //start讓調(diào)度線程啟動(dòng)【調(diào)度線程可以從jobstore中獲取快要執(zhí)行的trigger,然后獲取trigger關(guān)聯(lián)的job,執(zhí)行job】
            scheduler.Start();
            //將job和trigger注冊(cè)到scheduler中
            scheduler.ScheduleJob(job, trigger).Wait();
            Console.ReadKey();
        }
    }

    #region MyJob

    /// 

    /// 一個(gè)簡(jiǎn)單的Job,所有的Job都要實(shí)現(xiàn)IJob接口
    /// 
    public class MyJob : IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
            await Task.Run(() =>
            {
                Console.WriteLine("hello quartz!");
                //JobDetail的key就是job的分組和job的名字
                Console.WriteLine($"JobDetail的組和名字:{context.JobDetail.Key}");
                Console.WriteLine();
            });
        }
    }

    #endregion MyJob
}

通過Quartz來調(diào)度一個(gè)任務(wù)十分簡(jiǎn)單,執(zhí)行結(jié)果如下:

四、Quartz.net的概念和基本用法

1.TriggerBuilder介紹

在Quartz中Trigger的作用是定義Job何時(shí)執(zhí)行。

Quartz.net提供了四種觸發(fā)策略:SimpleSchedule,CalendarIntervalSchedule,DailyTimeIntervalSchedule和CronSchedule。

TriggerBuilder顧名思義就是用來創(chuàng)建Trigger的。

1、SimpleSchedule

Simpleschedule 是最簡(jiǎn)單的一種觸發(fā)策略,它的作用類似于timer,可以設(shè)置間隔幾秒/幾分鐘/幾小時(shí)執(zhí)行一次,如創(chuàng)建一秒執(zhí)行一次的觸發(fā)器如下

ITrigger trigger = TriggerBuilder.Create()
        .StartNow()
        .WithIdentity("tname1", "group1")
        .WithSimpleSchedule(x => x.WithIntervalInSeconds(1)  //設(shè)置時(shí)間間隔,時(shí)分秒              
        .WithRepeatCount(3))       //設(shè)置執(zhí)行次數(shù),總共執(zhí)行3+1次,
    .Build();

2、CalendarIntervalSchedule

CalendarIntervalSchedule擴(kuò)展了Simplescheduler的功能,Simplescheduler只能在時(shí)分秒的維度上指定時(shí)間間隔,那么就有一個(gè)問題,如果我們想一個(gè)月執(zhí)行一次怎么辦呢?要知道每個(gè)月的天數(shù)是不一樣的,用SimpleSchedule實(shí)現(xiàn)起來就十分麻煩了。 
CalendarIntervalSchedule可以實(shí)現(xiàn)時(shí)分秒天周月年的維度上執(zhí)行輪詢。如創(chuàng)建一個(gè)月執(zhí)行一次的觸發(fā)器如下:這樣

ITrigger trigger = TriggerBuilder.Create()
    .StartAt(DateTimeOffset.Parse("2018/1/6 13:17:00"))
    .WithIdentity("tname1", "group1")
    .WithCalendarIntervalSchedule(x => x.WithIntervalInMonths(1))  //一月執(zhí)行一次
    .Build();

3、DailyTimeIntervalSchedule

DailyTimeIntervalSchedule主要用于指定每周的某幾天執(zhí)行,如我們想讓每周的周六周日的8:00-20:00,每?jī)擅雸?zhí)行一次,創(chuàng)建觸發(fā)器如下:

ITrigger trigger =TriggerBuilder.Create()
        .StartAt(DateTimeOffset.Parse("2018/1/6 13:17:00"))
        .WithIdentity("tname1", "group1")
        .WithDailyTimeIntervalSchedule(x => x.OnDaysOfTheWeek(new DayOfWeek[] { DayOfWeek.Saturday, DayOfWeek.Sunday }) //周六和周日
            .StartingDailyAt(TimeOfDay.HourMinuteAndSecondOfDay(8, 00, 00)) //8點(diǎn)開始
            .EndingDailyAt(TimeOfDay.HourMinuteAndSecondOfDay(20, 00, 00))  //20點(diǎn)結(jié)束
            .WithIntervalInSeconds(2)                                       //兩秒執(zhí)行一次,可設(shè)置時(shí)分秒維度
            .WithRepeatCount(3))                                            //一共執(zhí)行3+1次
    .Build();

4、CronSchedule

CronSchedule是應(yīng)用最多的觸發(fā)策略,通過Cron表達(dá)是我們可以輕松地表示任意的時(shí)間節(jié)點(diǎn),下邊的代碼創(chuàng)建了一個(gè)每隔5秒執(zhí)行一次的觸發(fā)器

ITrigger trigger = TriggerBuilder.Create()
    .StartAt(DateTimeOffset.Parse("2018/1/6 13:17:00"))
    .WithIdentity("tname1", "group1")
    .WithCronSchedule("3/5 * * * * ?")  //五秒執(zhí)行一次
    .Build();

5、Cron表達(dá)式

cron表達(dá)式有七個(gè)部分組成,以此是秒、分、時(shí)、天、月、周、年,其中年是可選的。Cron表達(dá)式對(duì)特殊字符的大小寫不敏感,對(duì)代表星期的縮寫英文大小寫也不敏感。

  • 星號(hào)(*):可用在所有字段中,表示對(duì)應(yīng)時(shí)間域的每一個(gè)時(shí)刻。例如, 在分鐘字段時(shí),表示“每分鐘”;
  • 問號(hào)(?):該字符只在日期和星期字段中使用,它通常指定為“無意義的值”,相當(dāng)于點(diǎn)位符;
  • 減號(hào)(-):表達(dá)一個(gè)范圍,如在小時(shí)字段中使用“10-12”,則表示從10到12點(diǎn),即10,11,12;
  • 逗號(hào)(,):表達(dá)一個(gè)列表值,如在星期字段中使用“MON,WED,FRI”,則表示星期一,星期三和星期五;
  • 斜杠(/):x/y表達(dá)一個(gè)等步長(zhǎng)序列,x為起始值,y為增量步長(zhǎng)值。如5/15在分鐘字段中表示5,20,35,50;
  • L:該字符只在日期和星期字段中使用,代表“Last”的意思,但它在兩個(gè)字段中意思不同。L在日期字段中,表示這個(gè)月份的最后一天,如一月的31號(hào),非閏年二月的28號(hào);如果L用在星期中,則表示星期六,等同于7。但是,如果L出現(xiàn)在星期字段里,而且在前面有一個(gè)數(shù)值X,則表示“這個(gè)月的最后X天”,例如,6L表示該月的最后星期五;
  • W:該字符只能出現(xiàn)在日期字段里,是對(duì)前導(dǎo)日期的修飾,表示離該日期最近的工作日。例如15W表示離該月15號(hào)最近的工作日,如果該月15號(hào)是星期六,則匹配14號(hào)星期五;如果15日是星期日,則匹配16號(hào)星期一;如果15號(hào)是星期二,那結(jié)果就是15號(hào)星期二。但必須注意關(guān)聯(lián)的匹配日期不能夠跨月,如你指定1W,如果1號(hào)是星期六,結(jié)果匹配的是3號(hào)星期一,而非上個(gè)月最后的那天。W字符串只能指定單一日期,而不能指定日期范圍;
  • LW組合:在日期字段可以組合使用LW,它的意思是當(dāng)月的最后一個(gè)工作日;
  • 井號(hào)(#):該字符只能在星期字段中使用,表示當(dāng)月某個(gè)工作日。如6#3表示當(dāng)月的第三個(gè)星期五(6表示星期五,#3表示當(dāng)前的第三個(gè)),而4#5表示當(dāng)月的第五個(gè)星期三,假設(shè)當(dāng)月沒有第五個(gè)星期三,忽略不觸發(fā);

一些例子:

  • 0 0 12 * * ?:每天12點(diǎn)運(yùn)行
  • 0 15 10 ? * *:每天10:15運(yùn)行
  • 0 15 10 * * ?:每天10:15運(yùn)行
  • 0 15 10 * * ? *:每天10:15運(yùn)行
  • 0 15 10 * * ? 2008:在2008年的每天10:15運(yùn)行
  • 0 * 14 * * ?:每天14點(diǎn)到15點(diǎn)之間每分鐘運(yùn)行一次,開始于14:00,結(jié)束于14:59。
  • 0 0/5 14 * * ?:每天14點(diǎn)到15點(diǎn)每5分鐘運(yùn)行一次,開始于14:00,結(jié)束于14:55。
  • 0 0/5 14,18 * * ?:每天14點(diǎn)到15點(diǎn)每5分鐘運(yùn)行一次,此外每天18點(diǎn)到19點(diǎn)每5鐘也運(yùn)行一次。
  • 0 0-5 14 * * ?:每天14:00點(diǎn)到14:05,每分鐘運(yùn)行一次。
  • 0 10,44 14 ? 3 WED:3月每周三的14:10分和14:44執(zhí)行。
  • 0 15 10 ? * MON-FRI:每周一,二,三,四,五的10:15分運(yùn)行。
  • 0 15 10 15 * ?:每月15日10:15分運(yùn)行。
  • 0 15 10 L * ?:每月最后一天10:15分運(yùn)行。
  • 0 15 10 ? * 6L:每月最后一個(gè)星期五10:15分運(yùn)行。
  • 0 15 10 ? * 6L 2007-2009:在2007,2008,2009年每個(gè)月的最后一個(gè)星期五的10:15分運(yùn)行。
  • 0 15 10 ? * 6#3:每月第三個(gè)星期五的10:15分運(yùn)行。

2.Scheduler介紹

調(diào)度器scheduler是Quartz中的獨(dú)立工作容器,所有的Trigger和Job都需要注冊(cè)到scheduler中才能工作。我們可以通過SchedulerFactory來獲取scheduler實(shí)例。如下:

//1.獲取默認(rèn)的標(biāo)準(zhǔn)Scheduler引用
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;


//2.通過代碼配置scheduler
 NameValueCollection properties = new NameValueCollection
            {
                //scheduler的名字
                ["quartz.scheduler.instanceName"] = "MyScheduler",
                // 設(shè)置線程池中線程個(gè)數(shù)為20個(gè)
                ["quartz.threadPool.threadCount"] = "20",
                ["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz",
                //JobStore類型為內(nèi)存存儲(chǔ)
                ["quartz.jobStore.type"] = "Quartz.Simpl.RAMJobStore, Quartz"
            };
            ISchedulerFactory factroy = new StdSchedulerFactory(properties);
            IScheduler scheduler= await factroy .GetScheduler();

這里列一些會(huì)經(jīng)常用的到方法,方法比較簡(jiǎn)單直觀,就不一一展示了。

scheduler.PauseJob(JobKey.Create("jobname", "groupname"));//暫停job
          scheduler.ResumeJob(JobKey.Create("jobname", "groupname"));//重新啟動(dòng)job
          scheduler.DeleteJob(JobKey.Create("jobname", "groupname"));//刪除job
          scheduler.PauseTrigger(new TriggerKey("triggername", "groupname"));//暫停trigger
          scheduler.ResumeTrigger(new TriggerKey("triggername", "groupname"));//重新啟動(dòng)trigger
          scheduler.UnscheduleJob(new TriggerKey("triggername", "groupname"));//刪除trigger
          scheduler.GetTriggersOfJob(JobKey.Create("jobname", "groupname"));//獲取一個(gè)job的所有key    
          scheduler.Standby();  //暫停所有的觸發(fā)器,可通過shceduler.Start()重啟
          scheduler.Shutdown(); //關(guān)閉scheduler,釋放資源。通過Shutdown()關(guān)閉后,不能通過Start()重啟
          scheduler.GetMetaData();//獲取scheduler的元數(shù)據(jù)
          scheduler.Clear();//清空容器中所有的IJob,ITrigger

         //調(diào)度多個(gè)任務(wù)
         Dictionary> triggersAndJobs = new Dictionary>();
         triggersAndJobs.Add(job1, new List() { trigger1,trigger2});
         triggersAndJobs.Add(job2, new List() { trigger3});
         await scheduler.ScheduleJobs(triggersAndJobs, true);

3.Calendar介紹

通過上邊的介紹,我們知道通過觸發(fā)器Trigger可以設(shè)置Job的執(zhí)行時(shí)間,但是有時(shí)候只使用Trigger來調(diào)度比較麻煩,如一年中每天都執(zhí)行,但是元旦、圣誕節(jié)和情人節(jié)這三天不執(zhí)行。使用trigger也可以實(shí)現(xiàn),但是比較麻煩,如果我們有一種方法可以方便地排除這三天就好辦了,Calendar主要作用就是為了排除Trigger中一些特定的時(shí)間節(jié)點(diǎn)??匆粋€(gè)簡(jiǎn)單的栗子:

    class Program
    {
        static void Main(string[] args)
        {
            //調(diào)度器
            IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;
            //JobDetail
            IJobDetail job = JobBuilder
                                .Create()
                                .Build();

            //獲取一個(gè)Calendar實(shí)例
            DailyCalendar calendar = new DailyCalendar(DateBuilder.DateOf(19, 0, 0).DateTime, DateBuilder.DateOf(23, 0, 0).DateTime);//21~23點(diǎn)不執(zhí)行
            //將Calendar注冊(cè)到Scheduler中
            scheduler.AddCalendar("myCalendar", calendar, true, true);//參數(shù)依次是:calendarname,calendar,是否替換同名clendar,是否更新trigger
            //獲取一個(gè)觸發(fā)器,并將calendar綁定到觸發(fā)器上
            ITrigger trigger =
                          TriggerBuilder.Create()
                               .StartNow()
                               .WithIdentity("tname1", "group1")
                               .WithCronSchedule("0/2 * * * * ?")  //2秒執(zhí)行一次
                               .ModifiedByCalendar("myCalendar")   //把Calendar綁定到trigger
                               .Build();

            //start讓調(diào)度線程啟動(dòng)
            scheduler.Start();
            //將job添加到調(diào)度器中,同時(shí)將trigger綁定到j(luò)ob
            scheduler.ScheduleJob(job, trigger).Wait();
            Console.ReadKey();
        }
    }

    #region MyJob,繼承IJob接口
    /// 

    /// 一個(gè)簡(jiǎn)單的Job
    /// 
    public class MyJob : IJob
    {public Task Execute(IJobExecutionContext context)
        {
            return Task.Run(() =>
            {
                Console.WriteLine($"觸發(fā)時(shí)間:{context.ScheduledFireTimeUtc?.LocalDateTime},下次觸發(fā)時(shí)間:{context.NextFireTimeUtc?.LocalDateTime}");
            });
        }

    }
    #endregion

執(zhí)行結(jié)果:

使用Calendar的流程是:首先獲取一個(gè)Calendar實(shí)例,然后將Calendar注冊(cè)到scheduler容器中,在將Calendar綁定到觸發(fā)器上即可。

Quartz.net中一共提供了六種Calendar,六種Calendar的用法大同小異,列舉如下:

【1】DailyCalendar 用于排除一天中的某一段時(shí)間

DailyCalendar calendar = new DailyCalendar(DateBuilder.DateOf(19, 0, 0).DateTime, DateBuilder.DateOf(23, 0, 0).DateTime);//21~23點(diǎn)不執(zhí)行

【2】WeeklyCalendar 用于排除一周中的某幾天

WeeklyCalendar calendar = new WeeklyCalendar();
 calendar.SetDayExcluded(DayOfWeek.Sunday, true);//周日不執(zhí)行 
//注:如果想讓周日恢復(fù)執(zhí)行,執(zhí)行代碼:  calendar.SetDayExcluded(DayOfWeek.Sunday, false);

【3】HolidayCalendar 用于排除某些日期 

HolidayCalendar calendar = new HolidayCalendar();
calendar.AddExcludedDate(DateTime.Parse("2018/1/2")); //2018年1月2號(hào)不執(zhí)行
//注:如果想讓2019/1/9恢復(fù)執(zhí)行,執(zhí)行代碼:  calendar.RemoveExcludedDate(DateTime.Parse("2018/1/2"));

【4】MonthlyCalendar 用于排除每個(gè)月的某天*************************************

MonthlyCalendar calendar = new MonthlyCalendar();
calendar.SetDayExcluded(8, true); //每個(gè)月的8號(hào)不執(zhí)行
//注:如果想讓8號(hào)恢復(fù)執(zhí)行,執(zhí)行代碼:  calendar.SetDayExcluded(8, false);

【5】AnnualCalendar 用于排除一年中的某些天*************************************

AnnualCalendar calendar = new AnnualCalendar();
calendar.SetDayExcluded(DateTime.Parse("2018/1/2"), true);//每年1月2號(hào)不執(zhí)行
//注:如果想讓1月8號(hào)恢復(fù)執(zhí)行,執(zhí)行代碼:   calendar.SetDayExcluded(DateTime.Parse("2018/1/2"),true);

【6】CronCalendar 用于排除cron表達(dá)式表示的時(shí)間***************************

CronCalendar calendar = new CronCalendar("* * * 2 1 ?"); //每年的1月2號(hào)不執(zhí)行

4.Listener介紹

TriggerListener和JobListener用法類似,這里以JobListener為例來介紹Quartz中的Listener。JobListener用于在Job執(zhí)行前、后和被拒絕時(shí)執(zhí)行一些動(dòng)作,和Asp.net中的filter很相似,用法并不復(fù)雜,看一個(gè)簡(jiǎn)單的栗子:

using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using System;
using System.Collections.Specialized;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            //獲取調(diào)度器
            NameValueCollection pairs = new NameValueCollection() { { "quartz.threadPool.ThreadCount", "30" } };
            StdSchedulerFactory factory = new StdSchedulerFactory(pairs);
            IScheduler scheduler = factory.GetScheduler().Result;

            //創(chuàng)建Job
            IJobDetail job = JobBuilder
                                .Create()                     //獲取JobBuilder
                                .WithIdentity("jobname1", "group1")  //添加Job的名字和分組
                                .Build();                            //生成IJobDetail

            //創(chuàng)建rigger
            ITrigger trigger =  TriggerBuilder.Create()                                  //獲取JobBuilder
                              .StartAt(DateBuilder.TodayAt(01, 00, 00))  //開始時(shí)間,今天的1點(diǎn)(hh,mm,ss),可使用StartNow()
                              .WithPriority(10)                          //優(yōu)先級(jí),當(dāng)觸發(fā)時(shí)間一樣時(shí),優(yōu)先級(jí)大的觸發(fā)器先執(zhí)行
                              .WithIdentity("tname1", "group1")          //添加名字和分組
                              .WithSimpleSchedule(x => x.WithIntervalInSeconds(1)
                                                        .RepeatForever()
                                                        .Build())
                              .Build();

            //啟動(dòng)調(diào)度器
            scheduler.Start();

            //myJobListener監(jiān)控所有的job
            scheduler.ListenerManager.AddJobListener(new MyJobListener(), GroupMatcher.AnyGroup());
            //將job添加到調(diào)度器中,同時(shí)將trigger綁定到j(luò)ob
            scheduler.ScheduleJob(job, trigger).Wait();
            Console.ReadKey();
        }
    }

    #region MyJob,繼承IJob接口

    /// 

    /// 一個(gè)簡(jiǎn)單的Job
    /// 
    public class MyJob : IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
            await Task.Run(() =>
            {
                Console.WriteLine("hello quartz!");
                Console.WriteLine($"ThreadPool中的線程個(gè)數(shù):{context.Scheduler.GetMetaData().Result.ThreadPoolSize}");
            });
        }
    }

    #endregion MyJob,繼承IJob接口

    #region myJobListener,繼承IJobListener接口

    /// 

    /// 一個(gè)簡(jiǎn)單的JobListener,triggerListener類似
    /// 
    public class MyJobListener : IJobListener
    {
        public string Name => "hello joblisener";

        //job被拒絕時(shí)執(zhí)行
        public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
        {
            await Task.Run(() => { });
        }

        //job開始前執(zhí)行
        public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
        {
            await Task.Run(() =>
            {
                Console.WriteLine("myjob-------------begin");
            });
        }

        //job完成后執(zhí)行
        public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
        {
            await Task.Run(() =>
            {
                Console.WriteLine("myjob---------------end");
                Console.WriteLine();
            });
        }

        #endregion myJobListener,繼承IJobListener接口
    }
}

上邊例子中,Listener執(zhí)行的動(dòng)作很簡(jiǎn)單,在Job執(zhí)行前打印begin,執(zhí)行后打印end,在實(shí)際開發(fā)中,我們可以在通過Listenter來記錄job的執(zhí)行日志,執(zhí)行結(jié)果如下:

五、Quartz.net通過數(shù)據(jù)庫(kù)持久化與配置集群。

1.JobStore介紹

學(xué)習(xí)持久化和集群前我們先了解一下Quartz.net中的JobStore,JobStore用于追蹤任務(wù)調(diào)度相關(guān)的所有數(shù)據(jù),如Job,Trigger,Calendar等。

Quartz.net 提供了兩種JobStore:RAMJobStore,AdoJobStore。

1、RAMJobStore:RAMJobStore是最簡(jiǎn)單的JobStore,顧名思義這種JobStore將所有的數(shù)據(jù)都存放在內(nèi)存中,這也是它運(yùn)行速度快的原因,但是弊端也很明顯:一旦應(yīng)用結(jié)束或者遇到斷電所有的數(shù)據(jù)都會(huì)丟失。

RAMJobStore是默認(rèn)的JobStore,我們也已通過下邊的代碼來顯式設(shè)置使用的JobStore為RAMJobStore。

quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz

2、AdoJobStore:AdoJobStore通過Ado.net將數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)中,因此可以解決斷電數(shù)據(jù)丟失的問題,但是因?yàn)橐x寫數(shù)據(jù)庫(kù)所以效率相對(duì)較低。

AdoJobStore官方支持的數(shù)據(jù)庫(kù)有:MySql,SqlServer,Sqllite,Oracle等,當(dāng)前AdoJobStore只有一種類型JobStoreTX,這一點(diǎn)不同于Jave版本,java版本還有JobStoreCMT類型。

2.Db持久化和集群配置

Quartz.net配置數(shù)據(jù)庫(kù)持久化和集群比較容易,可以分為簡(jiǎn)單的兩步:

第一步:添加數(shù)據(jù)庫(kù)表

我們首先要在數(shù)據(jù)庫(kù)中添加一系列的表(在Quartz項(xiàng)目的database/tables文件夾下可以找到各種數(shù)據(jù)庫(kù)表的生成腳本,Git地址https://github.com/quartznet/quartznet/tree/master/database/tables)。以Sqlserver為例,Sqlserver的數(shù)據(jù)表生成腳本如下:

(略)

在Sqlserver管理器中新建查詢,粘貼上邊的腳本,運(yùn)行即可生成Quartz的數(shù)據(jù)庫(kù)表,數(shù)據(jù)庫(kù)的結(jié)構(gòu)如下:

第二步:配置QuartzFactory屬性,直接看代碼:

    class Program
    {
        public static void Main(string[] args)
        {
            NameValueCollection pars = new NameValueCollection
            {
                //scheduler名字
                ["quartz.scheduler.instanceName"] = "MyScheduler",
                //線程池個(gè)數(shù)
                ["quartz.threadPool.threadCount"] = "20",
                //類型為JobStoreXT,事務(wù)
                ["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
                //JobDataMap中的數(shù)據(jù)都是字符串
                //["quartz.jobStore.useProperties"] = "true",
                //數(shù)據(jù)源名稱
                ["quartz.jobStore.dataSource"] = "myDS",
                //數(shù)據(jù)表名前綴
                ["quartz.jobStore.tablePrefix"] = "QRTZ_",
                //使用Sqlserver的Ado操作代理類
                ["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz",
                //數(shù)據(jù)源連接字符串
                ["quartz.dataSource.myDS.connectionString"] = "Server=[yourserver];Database=quartzDb;Uid=sa;Pwd=[yourpass]",
                //數(shù)據(jù)源的數(shù)據(jù)庫(kù)
                ["quartz.dataSource.myDS.provider"] = "SqlServer",
                //序列化類型
                ["quartz.serializer.type"] = "json",//binary
                //自動(dòng)生成scheduler實(shí)例ID,主要為了保證集群中的實(shí)例具有唯一標(biāo)識(shí)
                ["quartz.scheduler.instanceId"] = "AUTO",
                //是否配置集群
                ["quartz.jobStore.clustered"] = "true",
            };
            StdSchedulerFactory factory = new StdSchedulerFactory(pars);
            IScheduler scheduler = factory.GetScheduler().Result;
            scheduler.Start();
            IJobDetail job = JobBuilder.Create()
                .WithIdentity("job1", "g1")
                .Build();
            ITrigger trigger = TriggerBuilder.Create().WithIdentity("trigger1", "g1").WithCronSchedule("0/1 * * * * ?").Build();
            if (scheduler.CheckExists(job.Key).Result)
            {
                scheduler.DeleteJob(job.Key).Wait();
            }
            scheduler.ScheduleJob(job, trigger).Wait();
        }
    }

    public class MyJob : IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
            await Task.Run(() =>
            {
                Console.WriteLine($"hello! 當(dāng)前時(shí)間:{DateTime.Now}");
                Console.WriteLine($"觸發(fā)時(shí)間:{context.ScheduledFireTimeUtc?.LocalDateTime},下次觸發(fā)時(shí)間:{context.NextFireTimeUtc?.LocalDateTime}");
                Console.WriteLine();

            });
        }
    }

上邊的代碼配置信息在代碼中都有注釋,這個(gè)例子實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的任務(wù)調(diào)度:每秒打印一次任務(wù)的觸發(fā)時(shí)間和下次觸發(fā)時(shí)間。然后運(yùn)行項(xiàng)目即可,到這里Db持久化和集群都配置完成了。運(yùn)行程序Quartz會(huì)自動(dòng)在數(shù)據(jù)庫(kù)中記錄調(diào)度任務(wù)相關(guān)的數(shù)據(jù)。

Quartz自動(dòng)向數(shù)據(jù)庫(kù)寫入的trigger信息:

Quartz自動(dòng)向數(shù)據(jù)庫(kù)寫入的Job信息:

程序執(zhí)行結(jié)果:

到這里我們看到了Db持久化已經(jīng)實(shí)現(xiàn)了,但是上邊的例子,我們?cè)诖a中通過 ["quartz.jobStore.clustered"] = "true" 配置了集群,這個(gè)有什么用呢?

首先添加一個(gè)debug文件夾的副本

然后運(yùn)行這兩個(gè)文件夾下的xxx.exe文件(如果使用的是.net core,生成的是xxx.dll文件,進(jìn)入dll文件所在目錄,命令行運(yùn)行 dotnet xxx.dll 即可啟動(dòng)),運(yùn)行結(jié)果如下:

如上所示,運(yùn)行兩個(gè)xxx.exe(core中是dll)后,原文件和副本在同一時(shí)間只有一個(gè)在運(yùn)行,所以我們調(diào)度的任務(wù)沒有重復(fù)執(zhí)行。如果我們關(guān)掉正在執(zhí)行的那個(gè)程序,那么另一個(gè)程序會(huì)開始執(zhí)行。我們可以得出結(jié)論:Quartz的集群并不會(huì)造成任務(wù)重復(fù)執(zhí)行,而且當(dāng)一個(gè)服務(wù)器掛了后,另一個(gè)服務(wù)器會(huì)自動(dòng)開始執(zhí)行,這種機(jī)制大大增加了任務(wù)調(diào)度的容災(zāi)性能。

3.一些需要注意的地方

1.Quartz3.x支持async和await,為提高性能,我們最好將Job中的Execute方法都寫成異步方法;

2.不管使用的是RAMJobStore還是AdoJobStore,千萬不要通過代碼來直接操作JobStore(比如我們直接通過代碼修改數(shù)據(jù)庫(kù)中的數(shù)據(jù)),JobStore讓Quartz自動(dòng)操作即可。
無論使用場(chǎng)景是web應(yīng)用還是桌面程序,我們只使用Scheduler提供的接口方法來實(shí)現(xiàn)Job和Trigger等的增/刪/改/查/暫停/恢復(fù)即可。

到此這篇關(guān)于作業(yè)調(diào)度框架Quartz.net用法的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 在Linux上使用OpenCvSharp的過程詳解

    在Linux上使用OpenCvSharp的過程詳解

    在本次項(xiàng)目中,我們成功實(shí)現(xiàn)了在Linux上使用OpenCvSharp,并成功配置了OpenCvSharp依賴庫(kù),實(shí)現(xiàn)了在.NET 6.0環(huán)境下使用C#語(yǔ)言調(diào)用OpenCvSharp庫(kù),實(shí)現(xiàn)的圖片數(shù)據(jù)的讀取以及圖像色彩轉(zhuǎn)換,并進(jìn)行了圖像展示,感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • 在ASP.NET中重寫URL的代碼

    在ASP.NET中重寫URL的代碼

    在ASP.NET中重寫URL的代碼...
    2007-03-03
  • 寫一個(gè)含數(shù)字,拼音,漢字的驗(yàn)證碼生成類

    寫一個(gè)含數(shù)字,拼音,漢字的驗(yàn)證碼生成類

    本文和大家分享的是一個(gè)集成1:小寫拼音;2:大寫拼音;3:數(shù)字;4:漢字的驗(yàn)證碼生成類。本章例子也會(huì)有一個(gè)mvc使用驗(yàn)證碼校驗(yàn)的場(chǎng)景。具有一定的參考價(jià)值,下面跟著小編一起來看下吧
    2017-01-01
  • .NET 中Worker Service的使用入門

    .NET 中Worker Service的使用入門

    隨著 .NET Core 3.0 的發(fā)布,ASP.NET 團(tuán)隊(duì)引入了一個(gè)新的&nbsp;Worker Service&nbsp;項(xiàng)目模板,該模板作為 .NET SDK 的一部分發(fā)布。在本文中,我將向您介紹這個(gè)新模板,以及使用它開發(fā)的一些實(shí)際的服務(wù)示例。
    2021-05-05
  • Asp.Net、asp實(shí)現(xiàn)的搜索引擎網(wǎng)址收錄檢查程序

    Asp.Net、asp實(shí)現(xiàn)的搜索引擎網(wǎng)址收錄檢查程序

    這篇文章主要介紹了Asp.Net、asp實(shí)現(xiàn)的搜索引擎網(wǎng)址收錄檢查程序,即實(shí)現(xiàn)檢查一個(gè)網(wǎng)址是否被搜索引擎收錄功能的小程序,需要的朋友可以參考下
    2014-08-08
  • 淺談.net core 注入中的三種模式:Singleton、Scoped 和 Transient

    淺談.net core 注入中的三種模式:Singleton、Scoped 和 Transient

    這篇文章主要介紹了淺談.net core 注入中的三種模式:Singleton、Scoped 和 Transient,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-04-04
  • 使用 Salt + Hash 將密碼加密后再存儲(chǔ)進(jìn)數(shù)據(jù)庫(kù)

    使用 Salt + Hash 將密碼加密后再存儲(chǔ)進(jìn)數(shù)據(jù)庫(kù)

    如果你需要保存密碼(比如網(wǎng)站用戶的密碼),你要考慮如何保護(hù)這些密碼數(shù)據(jù),象下面那樣直接將密碼寫入數(shù)據(jù)庫(kù)中是極不安全的,因?yàn)槿魏慰梢源蜷_數(shù)據(jù)庫(kù)的人,都將可以直接看到這些密碼
    2012-12-12
  • ASP.NET的實(shí)用技巧詳細(xì)介紹

    ASP.NET的實(shí)用技巧詳細(xì)介紹

    本文介紹的是ASP.NET的實(shí)用技巧,從跟蹤頁(yè)面,表單數(shù)據(jù)的服務(wù)器端驗(yàn)證和跳過表單驗(yàn)證等方面為大家介紹的。希望對(duì)你有幫助,一起來看。
    2015-10-10
  • 簡(jiǎn)單好用的ASP.NET分頁(yè)類(支持AJAX、自定義文字)

    簡(jiǎn)單好用的ASP.NET分頁(yè)類(支持AJAX、自定義文字)

    這篇文章主要介紹了簡(jiǎn)單好用的ASP.NET分頁(yè)類(支持AJAX、自定義文字),本文直接給出實(shí)現(xiàn)代碼和使用方法,需要的朋友可以參考下
    2015-06-06
  • ASP.NET 5中使用AzureAD實(shí)現(xiàn)單點(diǎn)登錄

    ASP.NET 5中使用AzureAD實(shí)現(xiàn)單點(diǎn)登錄

    本文給大家介紹的是在ASP.NET 5中使用AzureAD實(shí)現(xiàn)單點(diǎn)登錄的方法和示例,有需要的小伙伴可以參考下。
    2015-07-07

最新評(píng)論