<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ASP.NET Core托管運(yùn)行Quartz.NET作業(yè)調(diào)度詳解

          共 6407字,需瀏覽 13分鐘

           ·

          2021-01-19 10:23


          Quartz.NET這么NB的作業(yè)調(diào)度系統(tǒng),不會(huì)還行?

          ?

          今天介紹一下Quartz.NET的托管運(yùn)行,官網(wǎng)傳送門。

          一、前言

          Quartz.NET,按官網(wǎng)上的說法,是一款功能齊全的任務(wù)調(diào)度系統(tǒng),從小型應(yīng)用到大型企業(yè)級系統(tǒng)都能適用。在眾多項(xiàng)目中,Quartz.NET以可靠、集群的方式,被用作在定時(shí)器上運(yùn)行后臺任務(wù)的一種方式。

          Quartz.NET主要完成兩個(gè)方面的內(nèi)容:

          1. 基于時(shí)間計(jì)劃的后臺作業(yè);

          2. 基于因時(shí)間計(jì)劃的觸發(fā)的任務(wù)運(yùn)行。

          ?

          ASP.NET Core本身對于通過托管服務(wù)運(yùn)行后臺任務(wù)就支持的很好。當(dāng)ASP.NET啟動(dòng)托管服務(wù)時(shí),應(yīng)用程序啟動(dòng),并在生命周期內(nèi)在后臺運(yùn)行。通過創(chuàng)建Quartz.NET托管服務(wù),可以使用標(biāo)準(zhǔn)的.Net Core托管服務(wù),在后臺運(yùn)行任務(wù)。

          Quartz.NET可以創(chuàng)建定時(shí)的任務(wù),例如每十分鐘運(yùn)行一個(gè)任務(wù)。除此之外,Quartz.NET還可以通過Cron觸發(fā)器,定義任務(wù)在特定的日子或特定的時(shí)間運(yùn)行,例如每天凌晨兩點(diǎn)執(zhí)行一個(gè)任務(wù)。它還允許以集群的方式運(yùn)行應(yīng)用程序的多個(gè)實(shí)例,以便在任何時(shí)間確保只有一個(gè)實(shí)例運(yùn)行給定的任務(wù)。

          ?

          下面,就針對這些特性和功能,進(jìn)行詳細(xì)的說明。


          二、安裝Quartz.NET

          Quartz.NET提供了NuGet包,所以安裝很簡單:

          %?dotnet?add?package?quartz

          這是個(gè)司機(jī)就知道,不詳說了。

          看一下安裝后的.csproj文件內(nèi)容:

          <Project?Sdk="Microsoft.NET.Sdk.Web">
          ??<PropertyGroup>
          ????<TargetFramework>netcoreapp3.1TargetFramework>
          ??PropertyGroup>
          ??<ItemGroup>
          ????<PackageReference?Include="quartz"?Version="3.2.2"?/>
          ??ItemGroup>
          Project>

          三、通過IJob創(chuàng)建任務(wù)類

          我們用個(gè)例子來說明 - 創(chuàng)建一個(gè)Demo的實(shí)現(xiàn)。它將寫入ILogger<>。我們會(huì)使用Quartz.NET的接口IJob來實(shí)現(xiàn),并使用依賴注入將日志注入到構(gòu)造函數(shù)中。

          [DisallowConcurrentExecution]
          public?class?DemoJob?:?IJob
          {
          ????private?readonly?ILogger?_logger;
          ????public?DemoJob(ILogger?logger)
          ????
          {
          ????????_logger?=?logger;
          ????}

          ????public?Task?Execute(IJobExecutionContext?context)
          ????
          {
          ????????_logger.LogInformation("Demo?!");
          ????????return?Task.CompletedTask;
          ????}
          }

          在類的前面,我用了一個(gè)DisallowConcurrentExecution屬性。這個(gè)屬性可以防止Quartz.NET同時(shí)運(yùn)行相同的作業(yè)。

          四、通過IJobFactory創(chuàng)建任務(wù)工廠

          通常情況下,Quartz.NET會(huì)使用Activator.CreateInstance來創(chuàng)建作業(yè)的實(shí)例。

          在我們這個(gè)例子里,我們換一種方式。使用IJobFactory實(shí)現(xiàn),并與ASP.NET Core的依賴注入容器掛鉤。

          public?class?SingletonJobFactory?:?IJobFactory
          {
          ????private?readonly?IServiceProvider?_serviceProvider;
          ????public?SingletonJobFactory(IServiceProvider?serviceProvider)
          ????
          {
          ????????_serviceProvider?=?serviceProvider;
          ????}

          ????public?IJob?NewJob(TriggerFiredBundle?bundle,?IScheduler?scheduler)
          ????
          {
          ????????return?_serviceProvider.GetRequiredService(bundle.JobDetail.JobType)?as?IJob;
          ????}

          ????public?void?ReturnJob(IJob?job)
          ????
          {
          ????}
          }

          這個(gè)IJobFactory的實(shí)現(xiàn),在構(gòu)造函數(shù)中引入IServiceProvider,并實(shí)現(xiàn)接口。

          接口中,最重要的是NewJob()方法。這個(gè)方法需要返回Quartz.NET調(diào)度器請求的IJob。在我們的例子中,我們直接委托給IServiceProvider,并讓DI容器找到所需的實(shí)例。

          ReturnJob()方法是調(diào)度程序返回和銷毀IJobFactory創(chuàng)建的作業(yè)的地方。不過,因?yàn)槲覀兪褂昧?code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: inherit;line-height: inherit;border-radius: 4px;color: rgb(233, 105, 0);background-color: rgb(248, 248, 248);">IServiceProvider,而它沒有提供這樣的處理。所以,從安全的角度,應(yīng)該使用單例作業(yè)。

          五、配置作業(yè)

          在第三節(jié)中,我們創(chuàng)建了一個(gè)IJob的實(shí)現(xiàn)。這個(gè)實(shí)現(xiàn)直接使用就可以。

          但是,我們這兒要加點(diǎn)內(nèi)容。我們把Quartz的托管服務(wù)做成一個(gè)通用實(shí)現(xiàn),來調(diào)度任意的作業(yè)。因此,我們創(chuàng)建一個(gè)簡單的DTO,并使用它來定義一個(gè)給定作業(yè)類型的時(shí)間器調(diào)度:

          public?class?JobSchedule
          {

          ????public?JobSchedule(Type?jobType,?string?cronExpression)
          ????
          {
          ????????JobType?=?jobType;
          ????????CronExpression?=?cronExpression;
          ????}

          ????public?Type?JobType?{?get;?}
          ????public?string?CronExpression?{?get;?}
          }

          在這個(gè)類中,JobType是一個(gè)作業(yè)的類型,例如本例子中的DemoJob。CronExpression是一個(gè)Cron的表達(dá)式。

          Cron表達(dá)式允許復(fù)雜的計(jì)時(shí)器調(diào)度,所以可以設(shè)置規(guī)則,比如每個(gè)月的5號和20號,在早上8點(diǎn)到10點(diǎn)之間每半小時(shí)觸發(fā)一次。

          關(guān)于Quartz.NETCron表達(dá)式,可以在這兒查到。

          注意:不同操作系統(tǒng)使用的Cron表達(dá)式有一定的區(qū)別,寫表達(dá)式的時(shí)候一定要注意這一點(diǎn)。

          ?

          然后,我們把作業(yè)添加到Startup.ConfigureServices()中:

          public?void?ConfigureServices(IServiceCollection?services)
          {
          ????services.AddControllers();

          ????services.AddSingleton();
          ????services.AddSingleton();

          ????services.AddSingleton();
          ????services.AddSingleton(new?JobSchedule(
          ????????jobType:?typeof(DemoJob),
          ????????cronExpression:?"0/5?*?*?*?*??"));?//?每5秒運(yùn)行一次
          }

          這段代碼向DI添加了四個(gè)單例對象:

          1. SingletonJobFactory,第四節(jié)的類,用于創(chuàng)建作業(yè)實(shí)例;

          2. ISchedulerFactory的一個(gè)實(shí)現(xiàn),是內(nèi)置的StdSchedulerFactory,用于處理調(diào)度和管理作業(yè);

          3. DemoJob作業(yè)本身;

          4. DemoJob的一個(gè)JobSchedule實(shí)例,該實(shí)例具有每5秒運(yùn)行一次的Cron表達(dá)式。

          ?

          現(xiàn)在,主要代碼已經(jīng)完成,就差Quartz托管服務(wù)了。

          六、創(chuàng)建Quartz托管服務(wù)

          QuartzHostedService是自己創(chuàng)建的Quartz托管服務(wù),繼承于IHostedService,用于設(shè)置Quartz調(diào)度器,并在后臺啟動(dòng)它。

          先看一下完整的代碼:

          public?class?QuartzHostedService?:?IHostedService
          {
          ????private?readonly?ISchedulerFactory?_schedulerFactory;
          ????private?readonly?IJobFactory?_jobFactory;
          ????private?readonly?IEnumerable?_jobSchedules;

          ????public?QuartzHostedService(ISchedulerFactory?schedulerFactory,?IJobFactory?jobFactory,?IEnumerable?jobSchedules)
          ????
          {
          ????????_schedulerFactory?=?schedulerFactory;
          ????????_jobSchedules?=?jobSchedules;
          ????????_jobFactory?=?jobFactory;
          ????}

          ????public?IScheduler?Scheduler?{?get;?set;?}

          ????public?async?Task?StartAsync(CancellationToken?cancellationToken)
          ????
          {
          ????????Scheduler?=?await?_schedulerFactory.GetScheduler(cancellationToken);
          ????????Scheduler.JobFactory?=?_jobFactory;?

          ????????foreach?(var?jobSchedule?in?_jobSchedules)
          ????????{
          ????????????var?job?=?CreateJob(jobSchedule);
          ????????????var?trigger?=?CreateTrigger(jobSchedule);

          ????????????await?Scheduler.ScheduleJob(job,?trigger,?cancellationToken);
          ????????}

          ????????await?Scheduler.Start(cancellationToken);
          ????}

          ????public?async?Task?StopAsync(CancellationToken?cancellationToken)
          ????
          {
          ????????await?Scheduler?.Shutdown(cancellationToken);
          ????}

          ????private?ITrigger?CreateTrigger(JobSchedule?schedule)
          ????
          {
          ????????return?TriggerBuilder
          ????????.Create()
          ????????.WithIdentity($"{schedule.JobType.FullName}.trigger")
          ????????.WithCronSchedule(schedule.CronExpression)
          ????????.WithDescription(schedule.CronExpression)
          ????????.Build();
          ????}

          ????private?IJobDetail?CreateJob(JobSchedule?schedule)
          ????
          {
          ????????var?jobType?=?schedule.JobType;
          ????????return?JobBuilder
          ????????????.Create(jobType)
          ????????????.WithIdentity(jobType.FullName)
          ????????????.WithDescription(jobType.Name)
          ????????????.Build();
          ????}
          }

          解釋一下這段代碼:

          這段代碼中,QuartzHostedService有三個(gè)依賴項(xiàng):Startup.ConfigureServices()中注入的ISchedulerFactoryIJobFactory,以及一個(gè)IEnumerable。在第五節(jié)的代碼中,我們只向DI添加了一個(gè)JobSchedule,就是DemoJob。我們也可以添加多個(gè)JobSchedule,他們都會(huì)在這個(gè)IEnumerable中被注入到托管服務(wù)中。

          StartAsync在應(yīng)用程序啟動(dòng)時(shí)被調(diào)用,它是我們配置Quartz的地方。我們首先創(chuàng)建IScheduler的一個(gè)實(shí)例,為它分配一個(gè)屬性供以后使用,并將調(diào)度程序的JobFactory設(shè)置為注入的實(shí)例:

          public?async?Task?StartAsync(CancellationToken?cancellationToken)
          {
          ????Scheduler?=?await?_schedulerFactory.GetScheduler(cancellationToken);
          ????Scheduler.JobFactory?=?_jobFactory;?
          ????//...?
          }

          然后,循環(huán)注入的作業(yè)調(diào)度,并在類的最后使用CreateJobCreateTrigger方法為每個(gè)作業(yè)創(chuàng)建一個(gè)IJobDetailITrigger。實(shí)際應(yīng)用中如果有別的需要,也可以通過擴(kuò)展JobSchedule?DTO來定制它。

          最后,在調(diào)度了所有作業(yè)之后,調(diào)用Scheduler.Start()來實(shí)際在后臺啟動(dòng)Quartz.NET調(diào)度器。當(dāng)應(yīng)用程序關(guān)閉時(shí),框架將調(diào)用StopAsync(),此時(shí)可以調(diào)用Scheduler.Shutdown()來安全地關(guān)閉調(diào)度程序進(jìn)程。

          ?

          全部完成后,我們啟動(dòng)QuartzHostedService

          public?void?ConfigureServices(IServiceCollection?services)
          {
          ????//?...
          ????services.AddHostedService();
          }

          ?

          運(yùn)行程序,可以看到結(jié)果:

          demo.DemoJob:?Information:?Demo?!
          info:?demo.DemoJob[0]
          ??????Demo?!
          demo.DemoJob:?Information:?Demo?!
          info:?demo.DemoJob[0]
          ??????Demo?!
          demo.DemoJob:?Information:?Demo?!
          info:?demo.DemoJob[0]
          ??????Demo?!
          demo.DemoJob:?Information:?Demo?!
          info:?demo.DemoJob[0]
          ??????Demo?!

          本文的代碼,在https://github.com/humornif/Demo-Code/tree/master/0029/demo

          喜歡就來個(gè)三連,讓更多人因你而受益



          往期精彩回顧




          【推薦】.NET Core開發(fā)實(shí)戰(zhàn)視頻課程?★★★

          .NET Core實(shí)戰(zhàn)項(xiàng)目之CMS 第一章 入門篇-開篇及總體規(guī)劃

          【.NET Core微服務(wù)實(shí)戰(zhàn)-統(tǒng)一身份認(rèn)證】開篇及目錄索引

          Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)

          .NET Core中的一個(gè)接口多種實(shí)現(xiàn)的依賴注入與動(dòng)態(tài)選擇看這篇就夠了

          10個(gè)小技巧助您寫出高性能的ASP.NET Core代碼

          用abp vNext快速開發(fā)Quartz.NET定時(shí)任務(wù)管理界面

          在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實(shí)現(xiàn)作業(yè)調(diào)度

          現(xiàn)身說法:實(shí)際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化

          關(guān)于C#異步編程你應(yīng)該了解的幾點(diǎn)建議

          C#異步編程看這篇就夠了


          瀏覽 48
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一级黄色电影免费看 | 亚洲色图在线播放 | 在线免费观看视频a | 国产黄色片精品AAWWW | 国产精品久久久久久久久久久久久 |