溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Quartz.Net使用方法是什么

發(fā)布時間:2021-12-23 16:23:29 來源:億速云 閱讀:132 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“Quartz.Net使用方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Quartz.Net使用方法是什么”吧!

    在項目的開發(fā)過程中,難免會遇見后需要后臺處理的任務(wù),例如定時發(fā)送郵件通知、后臺處理耗時的數(shù)據(jù)處理等,這個時候你就需要Quartz.Net了。

    Quartz.Net是純凈的,它是一個.Net程序集,是非常流行的Java作業(yè)調(diào)度系統(tǒng)Quartz的C#實現(xiàn)。

    Quartz.Net一款功能齊全的任務(wù)調(diào)度系統(tǒng),從小型應(yīng)用到大型企業(yè)級系統(tǒng)都能適用。功能齊全體現(xiàn)在觸發(fā)器的多樣性上面,即支持簡單的定時器,也支持Cron表達式;即能執(zhí)行重復(fù)的作業(yè)任務(wù),也支持指定例外的日歷;任務(wù)也可以是多樣性的,只要繼承IJob接口即可。

    對于小型應(yīng)用,Quartz.Net可以集成到你的系統(tǒng)中,對于企業(yè)級系統(tǒng),它提供了Routing支持,提供了Group來組織和管理任務(wù),此外還有持久化、插件功能、負載均衡和故障遷移等滿足不同應(yīng)用場景的需要。

    Hello Quartz.Net

    開始使用一個框架,和學(xué)習(xí)一門開發(fā)語言一樣,最好是從Hello World程序開始。

    首先創(chuàng)建一個示例程序,然后添加Quartz.Net的引用。

    Install-Package Quartz -Version 3.0.7

    我們使用的是當(dāng)前最新版本3.0.7進行演示。添加引用以后,來創(chuàng)建一個Job類HelloQuartzJob

    public class HelloQuartzJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Hello Quartz.Net");
            });
        }
    }

    這是個非常簡單的Job類,它在執(zhí)行時輸出文本Hello Quartz.Net。

    接下來,我們在程序啟動時創(chuàng)建調(diào)度器(Scheduler),并添加HelloQuartzJob的調(diào)度:

    static async Task MainAsync()
    {
        var schedulerFactory = new StdSchedulerFactory();
        var scheduler = await schedulerFactory.GetScheduler();
        await scheduler.Start();
        Console.WriteLine($"任務(wù)調(diào)度器已啟動");
    
        //創(chuàng)建作業(yè)和觸發(fā)器
        var jobDetail = JobBuilder.Create<HelloQuartzJob>().Build();
        var trigger = TriggerBuilder.Create()
                                    .WithSimpleSchedule(m => {
                                        m.WithRepeatCount(3).WithIntervalInSeconds(1);
                                    })
                                    .Build();
    
        //添加調(diào)度
        await scheduler.ScheduleJob(jobDetail, trigger);
    }

    然后運行程序,你會看到如下圖:

    Quartz.Net使用方法是什么

    通過演示可以看出,要執(zhí)行一個定時任務(wù),一般需要四步:

    • 創(chuàng)建任務(wù)調(diào)度器。調(diào)度器通常在應(yīng)用程序啟動時創(chuàng)建,一個應(yīng)用程序?qū)嵗ǔV恍枰粋€調(diào)度器即可。

    • 創(chuàng)建Job和JobDetail。Job是作業(yè)的類型,描述了作業(yè)是如何執(zhí)行的,這個類是由我們定義的;JobDetail是Quartz對作業(yè)的封裝,它包含Job類型,以及Job在執(zhí)行時用到的數(shù)據(jù),還包括是否要持久化、是否覆蓋已存在的作業(yè)等選項。

    • 創(chuàng)建觸發(fā)器。觸發(fā)器描述了在何時執(zhí)行作業(yè)。

    • 添加調(diào)度。當(dāng)完成以上三步以后,就可以對作業(yè)進行調(diào)度了。

    作業(yè):Job和JobDetail

    Job是作業(yè)的類型,描述了作業(yè)是如何執(zhí)行的,這個類型是由我們定義的,例如上文的HelloQuartzJob。Job實現(xiàn)IJob接口,而IJob接口只有一個Execute方法,參數(shù)context中包含了與當(dāng)前上下文中關(guān)聯(lián)的Scheduler、JobDetail、Trigger等。

    一個典型的Job定義如下:

    public class HelloQuartzJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Hello Quartz.Net");
            });
        }
    }

    JobData

    Job不是孤立存在的,它需要執(zhí)行的參數(shù),這些參數(shù)如何傳遞進來呢?我們來定義一個Job類進行演示。

    public class SayHelloJob : IJob
    {
        public string UserName { get; set; }
    
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Factory.StartNew(() =>
            {
                Console.WriteLine($"Hello {UserName}!");
            });
        }
    }

    SayHelloJob在執(zhí)行時需要參數(shù)UserName,這個參數(shù)被稱為JobData,Quartz.Net通過JobDataMap的方式傳遞參數(shù)。代碼如下:

    //創(chuàng)建作業(yè)
    var jobDetail = JobBuilder.Create<SayHelloJob>()
                                .SetJobData(new JobDataMap() {
                                    new KeyValuePair<string, object>("UserName", "Tom")
                                })
                                .Build();

    通過JobBuilder的SetJobData方法,傳入JobDataMap對象,JobDataMap對象中可以包含多個參數(shù),這些參數(shù)可以映射到Job類的屬性上。我們完善代碼運行示例,可以看到如下圖:

    Quartz.Net使用方法是什么

    JobDetail

    JobDetail是Quartz對作業(yè)的封裝,它包含Job類型,以及Job在執(zhí)行時用到的數(shù)據(jù),還包括是否孤立存儲、請求恢復(fù)作業(yè)等選項。

    JobDetail是通過JobBuilder進行創(chuàng)建的。例如:

    var jobDetail = JobBuilder.Create<SayHelloJob>()
                                .SetJobData(new JobDataMap() {
                                    new KeyValuePair<string, object>("UserName", "Tom")
                                })
                                .StoreDurably(true)
                                .RequestRecovery(true)
                                .WithIdentity("SayHelloJob-Tom", "DemoGroup")
                                .WithDescription("Say hello to Tom job")
                                .Build();

    參數(shù)說明:

    • SetJobData:設(shè)置JobData

    • StoreDurably:孤立存儲,指即使該JobDetail沒有關(guān)聯(lián)的Trigger,也會進行存儲

    • RequestRecovery:請求恢復(fù),指應(yīng)用崩潰后再次啟動,會重新執(zhí)行該作業(yè)

    • WithIdentity:作業(yè)的唯一標識

    • WithDescription:作業(yè)的描述信息

    除此之外,Quartz.Net還支持兩個非常有用的特性:

    • DisallowConcurrentExecution:禁止并行執(zhí)行,該特性是針對JobDetail生效的

    • PersistJobDataAfterExecution:在執(zhí)行完成后持久化JobData,該特性是針對Job類型生效的,意味著所有使用該Job的JobDetail都會在執(zhí)行完成后持久化JobData。

    持久化JobData

    我們來演示一下該PersistJobDataAfterExecution特性,在SayHelloJob中,我們新加一個字段RunSuccess,記錄任務(wù)是否執(zhí)行成功。

    首先在SayHelloJob添加特性:

    [PersistJobDataAfterExecution]
    public class SayHelloJob : IJob { }

    然后在創(chuàng)建JobDetail時添加JobData:

    var jobDetail = JobBuilder.Create<SayHelloJob>()
                                .SetJobData(new JobDataMap() {
                                    new KeyValuePair<string, object>("UserName", "Tom"),
                                    new KeyValuePair<string, object>("RunSuccess", false)
                                })

    在執(zhí)行時Job時,更新RunSuccess的值:

    public Task Execute(IJobExecutionContext context)
    {
        return Task.Factory.StartNew(() =>
        {
            Console.WriteLine($"Prev Run Success:{RunSuccess}");
            Console.WriteLine($"Hello {UserName}!");
    
            context.JobDetail.JobDataMap.Put("RunSuccess", true);
        });
    }

    接下來看一下執(zhí)行效果:

    Quartz.Net使用方法是什么

    觸發(fā)器:Trigger

    Trigger是觸發(fā)器,用來定制執(zhí)行作業(yè)。Trigger有兩種類型:SampleTrigger和CronTrigger,我們分別進行說明。

    SampleTrigger

    顧名思義,這是個簡單的觸發(fā)器,有以下特性:

    • 重復(fù)執(zhí)行:WithRepeatCount()/RepeatForever()

    • 設(shè)置間隔時間:WithInterval()

    • 定時執(zhí)行:StartAt()/StartNow()

    • 設(shè)定優(yōu)先級:WithPriority(),默認為5

    需要注意:當(dāng)Trigger到達StartAt指定的時間時會執(zhí)行一次,這一次執(zhí)行是不包含在WithRepeatCount中的。在我們上面的例子中可以看出,添加調(diào)度后會立即執(zhí)行一次,然后重復(fù)三次,最終執(zhí)行了四次。

    CronTrigger

    CronTrigger是通過Cron表達式來完成調(diào)度的。Cron表達式非常靈活,可以實現(xiàn)幾乎各種定時場景的需要。

    關(guān)于Cron表達式,大家可以移步 Quartz Cron表達式

    使用CronTrigger的示例如下:

    var trigger = TriggerBuilder.Create()
                                .WithCronSchedule("*/1 * * * * ?")
                                .Build();

    日歷:Calendar

    Calendar可以與Trigger進行關(guān)聯(lián),從Trigger中排出執(zhí)行計劃。例如你只希望在工作日執(zhí)行作業(yè),那么我們可以定義一個休息日的日歷,將它與Trigger關(guān)聯(lián),從而排出休息日的執(zhí)行計劃。

    Calendar示例代碼如下:

    var calandar = new HolidayCalendar();
    calandar.AddExcludedDate(DateTime.Today);
    
    await scheduler.AddCalendar("holidayCalendar", calandar, false, false);
    
    var trigger = TriggerBuilder.Create()
                            .WithCronSchedule("*/1 * * * * ?")
                            .ModifiedByCalendar("holidayCalendar")
                            .Build();

    在這個示例中,我們創(chuàng)建了HolidayCalendar日歷,然后添加排除執(zhí)行的日期。我們把今天添加到排除日期后,該Trigger今天將不會觸發(fā)。

    監(jiān)聽器

    • JobListeners

    • TriggerListeners

    • SchedulerListeners

    監(jiān)聽器是Quartz.Net的另外一個出色的功能,它允許我們編寫監(jiān)聽器達到在運行時獲取作業(yè)狀態(tài)、處理作業(yè)數(shù)據(jù)等功能。

    JobListener

    JobListener可以監(jiān)聽Job執(zhí)行前、執(zhí)行后、否決執(zhí)行的事件。我們通過代碼進行演示:

    public class MyJobListener : IJobListener
    {
        public string Name { get; } = nameof(MyJobListener);
    
        public Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            //Job即將執(zhí)行
            return Task.Factory.StartNew(() =>
            {
                Console.WriteLine($"Job: {context.JobDetail.Key} 即將執(zhí)行");
            });
        }
    
        public Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            return Task.Factory.StartNew(()=> {
                Console.WriteLine($"Job: {context.JobDetail.Key} 被否決執(zhí)行");
            });
        }
    
        public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
        {
            //Job執(zhí)行完成
            return Task.Factory.StartNew(() =>
            {
                Console.WriteLine($"Job: {context.JobDetail.Key} 執(zhí)行完成");
            });
        }
    }

    定義完成后,將MyJobListener添加到Scheduler中:

    scheduler.ListenerManager.AddJobListener(new MyJobListener(), GroupMatcher<JobKey>.AnyGroup());

    然后我們再運行程序,就可以看到Listener被調(diào)用了:

    Quartz.Net使用方法是什么

    通過圖片可以看到,JobToBeExecutedJobWasExecuted都被執(zhí)行了,JobExecutionVetoed沒有執(zhí)行,那么如何觸發(fā)JobExecutionVetoed呢?請繼續(xù)閱讀TriggerListener的演示。

    TriggerListener

    TriggerListener可以監(jiān)聽Trigger的執(zhí)行情況,我們通過代碼進行演示:

    public class MyTriggerListener : ITriggerListener
    {
        public string Name { get; } = nameof(MyTriggerListener);
    
        public Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }
    
        public Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }
    
        public Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }
    
        public Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(true);   //返回true表示否決Job繼續(xù)執(zhí)行
        }
    }

    MyTriggerListener添加到Scheduler中:

    scheduler.ListenerManager.AddTriggerListener(new MyTriggerListener(), GroupMatcher<TriggerKey>.AnyGroup());

    運行代碼可以看到如下效果:

    Quartz.Net使用方法是什么

    從圖片中可以看到,JobListener中的JobExecutionVetoed被執(zhí)行了。

    SchedulerListener

    ISchedulerListener提供了Job、Trigger管理的監(jiān)聽,與調(diào)度程序相關(guān)的事件包括:添加作業(yè)/觸發(fā)器,刪除作業(yè)/觸發(fā)器,調(diào)度程序中的嚴重錯誤,調(diào)度程序關(guān)閉的通知等。完整的接口定義如下:

    public interface ISchedulerListener
    {
        Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default);
        Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default);
        Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default);
        Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default);
        Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default);
        Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default);
        Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default);
        Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default);
        Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default);
        Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default);
        Task SchedulerInStandbyMode(CancellationToken cancellationToken = default);
        Task SchedulerShutdown(CancellationToken cancellationToken = default);
        Task SchedulerShuttingdown(CancellationToken cancellationToken = default);
        Task SchedulerStarted(CancellationToken cancellationToken = default);
        Task SchedulerStarting(CancellationToken cancellationToken = default);
        Task SchedulingDataCleared(CancellationToken cancellationToken = default);
        Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default);
        Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default);
        Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default);
        Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default);
        Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default);
    }

    添加SchedulerListener的代碼如下:

    scheduler.ListenerManager.AddSchedulerListener(mySchedListener);

    持久化:JobStore

    Quartz.Net支持Job的持久化操作,被稱為JobStore。默認情況下,Quartz將數(shù)據(jù)持久化到內(nèi)存中,好處是內(nèi)存的速度很快,壞處是無法提供負載均衡的支持,并且在程序崩潰后,我們將丟失所有Job數(shù)據(jù),對于企業(yè)級系統(tǒng)來說,壞處明顯大于好處,因此有必要將數(shù)據(jù)存儲在數(shù)據(jù)庫中。

    ADO.NET存儲

    Quartz使用ADO.NET訪問數(shù)據(jù)庫,支持的數(shù)據(jù)庫廠商非常廣泛:

    • SqlServer - .NET Framework 2.0的SQL Server驅(qū)動程序

    • OracleODP - Oracle的Oracle驅(qū)動程序

    • OracleODPManaged - Oracle的Oracle 11托管驅(qū)動程序

    • MySql - MySQL Connector / .NET

    • SQLite - SQLite ADO.NET Provider

    • SQLite-Microsoft - Microsoft SQLite ADO.NET Provider

    • Firebird - Firebird ADO.NET提供程序

    • Npgsql - PostgreSQL Npgsql

    數(shù)據(jù)庫的創(chuàng)建語句可以在Quartz.Net的源碼中找到:https://github.com/quartznet/quartznet/tree/master/database/tables

    我們可以通過配置文件來配置Quartz使用數(shù)據(jù)庫存儲:

    # job store
    quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
    quartz.jobStore.dataSource = quartz_store
    quartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.PostgreSQLDelegate, Quartz
    #quartz.jobStore.useProperties = true
    
    quartz.dataSource.quartz_store.connectionString = Server=localhost;Database=quartz_store;userid=quartz_net;password=xxxxxx;Pooling=true;MinPoolSize=1;MaxPoolSize=10;Timeout=15;SslMode=Disable;
    quartz.dataSource.quartz_store.provider = Npgsql

    負載均衡

    負載均衡是實現(xiàn)高可用的一種方式,當(dāng)任務(wù)量變大以后,單臺服務(wù)器很難滿足需要,使用負載均衡則使得系統(tǒng)具備了橫向擴展的能力,通過部署多個節(jié)點來增加處理Job的能力。

    Quartz.Net在使用負載均衡時,需要依賴ADO JobStore,意味著你需要使用數(shù)據(jù)庫持久化數(shù)據(jù)。然后我們可以使用以下配置完成負載均衡功能:

    quartz.jobStore.clustered = true
    quartz.scheduler.instanceId = AUTO
    • clustered:集群的標識

    • instanceId:當(dāng)前Scheduler實例的ID,每個示例的ID不能重復(fù),使用AUTO時系統(tǒng)會自動生成ID

    當(dāng)我們在多臺服務(wù)器上運行Scheduler實例時,需要設(shè)置服務(wù)器的時鐘時間,確保服務(wù)器時間是相同的。針對windows服務(wù)器,可以設(shè)置從網(wǎng)絡(luò)自動同步時間。

    通過Routing訪問Quartz實例

    通過Routing訪問Quartz實例的功能,為我們做系統(tǒng)分離提供了很好的途徑。

    我們可以通過以下配置實現(xiàn)Quartz的服務(wù)器端遠程訪問:

    # export this server to remoting context
    quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
    quartz.scheduler.exporter.port = 555
    quartz.scheduler.exporter.bindName = QuartzScheduler
    quartz.scheduler.exporter.channelType = tcp
    quartz.scheduler.exporter.channelName = httpQuartz

    然后我們在客戶端系統(tǒng)中配置訪問:

    quartz.scheduler.proxy = true
    quartz.scheduler.proxy.address = tcp://localhost:555/QuartzScheduler

    開發(fā)實踐

    理想中的任務(wù)調(diào)度系統(tǒng)應(yīng)該是一個后臺服務(wù),默無聲息的運行在系統(tǒng)后臺,業(yè)務(wù)系統(tǒng)通過接口完成對任務(wù)的添加、刪除等操作。在構(gòu)架Windows服務(wù)時,可以和TopShelf集成完成windows服務(wù)的開發(fā)。

    Install-Package Topshelf

    進行服務(wù)開發(fā)的另外一個問題是,Quartz本身是不支持依賴注入的,而解決依賴注入的問題,則可以使用Autofac,幸運的是已經(jīng)有大神完成了TopShelf與Autofac的集成,我們只需要使用即可。

    Install-Package Topshelf.Autofac

    Quartz.Net Job的添加有兩種方式:運行時動態(tài)添加和通過配置文件添加。這里推薦使用動態(tài)的方式進行添加(示例代碼是采用動態(tài)方式進行添加的),除非你的Job是相對固定的。

    而對Scheduler的配置可以采用配置文件的方式,方便在部署時進行維護。

    到此,相信大家對“Quartz.Net使用方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI