ASP.NETコアを搭載して簡単にサービスをホストしているジョブスケジューリングQuartz.NETを作成します。

この記事では、私が使用する方法を紹介しますASP.NETコアホスティングサービスの実行Quartz.NETジョブを。利点は、我々は簡単に私たちの仕事のアプリケーションが起動し、停止の動作状態を制御することができるということです。次に、私はシンプルを作成する方法を示しますIJob、カスタムをIJobFactoryして、ランタイムアプリケーションで実行するようになりましたQuartzHostedService私はまた、スコープのシングルトンクラスのサービスを、すなわち使用を注意を必要とする多くの問題を紹介します。

著者:ルウィッシュに従って

開始アドレス:https://www.cnblogs.com/yilezhu/p/12644208.html

参考英語住所:https://andrewlock.net/creating-a-quartz-net-hosted-service-with-asp-net-core/

はじめに - Quartz.NETは何ですか?

すべてのコンテンツQuartz.NETの基本的なコアの概要を説明し、次の図でQuartz.NETを見て前に導入されているものの初めに。

注意:このショーは、侵害は、連絡先を削除した場合、共有に学ぶためにBaiduの目的で取得します。

Quartz.NET

次のウェブサイトの説明:

Quartz.NETは、大規模なエンタープライズシステムへの小さなアプリケーションに最も適してからフル機能を備えたオープンソースのジョブスケジューリングシステムです。

多くのASP.NET開発者にとって、それは信頼性の高い、クラスター・アプローチでバックグラウンドタスクを実行しているタイマーで使用される好ましい方法です。Quartz.NETはまた、ASP.NETコアと一緒に非常によく似て使用すること - あなたが簡単にあなたのアプリケーションでそれを使用できるようにQuartz.NETは、.NET標準2.0をサポートしているため。

Quartz.NET二つの主要な概念があります。

  • ジョブこれは、実行中の特定の時刻表をYaoanというバックグラウンドタスクです。
  • スケジューラこれは、ジョブを実行するトリガーベース、時間ベースの計画を担当しています。

よるASP.NETコアホスティングサービスは、実行するための良いサポートがある「バックグラウンドタスクを。」ASP.NET Coreアプリケーションは、アプリケーションのライフサイクルに固有の背景を開始し、実行するために開始したときにサービスを管理していました。Quartz.NETホスティングサービスを作成することで、バックグラウンドでタスクを実行している標準のASP.NETコア・アプリケーションを使用することができます。

あなたが作成することもできますが、「通常の」バックオフィスサービス(例えば、タスクを実行するために10分ごとに)、しかしQuartz.NETは、より堅牢なソリューションを提供します。使用してcronのトリガーを、あなただけの一日の特定の時間帯(例えば、午前2時30分午前)の実行、または数日中のみ特定の操作、または実行の任意の組み合わせで、そのタスクを確保することができます。また、あなただけの任意の時点で1つのインスタンス(HA)を実行するために、アプリケーションの実行複数のインスタンスをクラスタ化することができます。

この記事では、ホスティングサービスで実行中のタイマー上にあるようにスケジュールされたジョブとQuartz.NETを作成するための基本をカバーします。

インストールQuartz.NET

Quartz.NETは、それはあなたのアプリケーションにインストールすることは非常に簡単で、.NETの標準2.0 NuGetパッケージです。このテストでは、私は、ASP.NETのコアプロジェクトを作成し、空のテンプレートを選択します。あなたはできるdotnet add package QuartzQuartz.NETパッケージをインストールします。プロジェクトのこの時間ビュー.csprojは次のように、しなければなりません。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Quartz" Version="3.0.7" />
  </ItemGroup>

</Project>

IJobを作成します。

我々が予定されている実用的なバックグラウンドの仕事のために、私たちは、注射に渡すILogger<>結果とコンソールに出力さ)で書き込み「こんにちは世界」を達成します。あなたは、単一の非同期含まれていることを認識しなければならないExecute()クォーツ・インタフェース・メソッドをIJob我々はロガーの依存性注入は、コンストラクタに注入されて使用し、ここで注意してください。

using Microsoft.Extensions.Logging;
using Quartz;
using System;
using System.Threading.Tasks;

namespace QuartzHostedService
{
    [DisallowConcurrentExecution]
    public class HelloWorldJob : IJob
    {
        private readonly ILogger<HelloWorldJob> _logger;

        public HelloWorldJob(ILogger<HelloWorldJob> logger)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        public Task Execute(IJobExecutionContext context)
        {
            _logger.LogInformation("Hello world by yilezhu at {0}!",DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            return Task.CompletedTask;
        }
    }
}

私はまた、使用[DisallowConcurrentExecution]仕事で飾らプロパティを。このプロパティの防止が同時に同じジョブを実行しようQuartz.NET

IJobFactoryを作成します。

次に、我々はクォーツの作成方法を指示する必要がありIJobインスタンスを。デフォルトでは、石英が使用されますActivator.CreateInstanceので、効果的にコールすると、ジョブ・インスタンスを作成しますnew HelloWorldJob()残念ながら、我々はコンストラクタ・インジェクションを使用し、そのために動作しないからです。代わりに、我々は、カスタムを提供することができますIJobFactory(ASP.NETコア依存性注入コンテナにフックをIServiceProviderして):

using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Quartz.Spi;
using System;

namespace QuartzHostedService
{
    public class SingletonJobFactory : IJobFactory
    {
        private readonly IServiceProvider _serviceProvider;

        public SingletonJobFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
        }

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

        public void ReturnJob(IJob job)
        {
            
        }
    }
}

植物になりますIServiceProviderパスコンストラクタ、および実装するIJobFactoryインタフェースを。ここで最も重要な方法があるであるNewJob()方法。この方法では、植物は、要求されたクォーツスケジューラを返さなければなりませんIJobこの実装では、直接私たちに委託IServiceProviderし、DIコンテナが目的のインスタンスを検索してみましょう。以下のためにGetRequiredService私たちが最後にキャストしなければならないので、非ジェネリックバージョンは、オブジェクトを返しますIJob

このReturnJob方法は、(すなわち破棄)このファクトリによって作成されたローカル・ジョブを返すために、スケジューラの試みです。残念ながら、ビルトインの使用IServiceProviderメカニズムは行っていません。私たちは、クォーツのために必要な、適切な新しいAPIを作成することはできませんIScopeService、我々は唯一の仕事の一つのケースを作成することができます。

これは非常に重要です。上記の実装を使用して、唯一の作成単一の実施形態(または一過性に)IJob実装が安全です。

コンフィギュレーションの求人

私はIJob唯一の実現を示すために、ここでは、私たちはユニバーサルクォーツホスティングサービスは、任意の数のジョブにも適用することが達成したいと考えています。この問題を解決するために、我々は、単純なDTOを作成しJobSchedule、タイマープログラムの特定のジョブタイプを定義するために使用されます。

using System;
using System.ComponentModel;

namespace QuartzHostedService
{
    /// <summary>
    /// Job调度中间对象
    /// </summary>
    public class JobSchedule
    {
        public JobSchedule(Type jobType, string cronExpression)
        {
            this.JobType = jobType ?? throw new ArgumentNullException(nameof(jobType));
            CronExpression = cronExpression ?? throw new ArgumentNullException(nameof(cronExpression));
        }
        /// <summary>
        /// Job类型
        /// </summary>
        public Type JobType { get; private set; }
        /// <summary>
        /// Cron表达式
        /// </summary>
        public string CronExpression { get; private set; }
        /// <summary>
        /// Job状态
        /// </summary>
        public JobStatus JobStatu { get; set; } = JobStatus.Init;
    }

    /// <summary>
    /// Job运行状态
    /// </summary>
    public enum JobStatus:byte
    {
        [Description("初始化")]
        Init=0,
        [Description("运行中")]
        Running=1,
        [Description("调度中")]
        Scheduling = 2,
        [Description("已停止")]
        Stopped = 3,

    }
}

ここではJobType、.NETタイプの仕事は、(私たちの例であるであるHelloWorldJob)、そしてCronExpressionあるQuartz.NETクーロン式あなたは以下の複雑なルールを設定することができるようにcron式は複雑なスケジューリングタイマーできるよう、例えば、「月刊5号と20号は午前十時8:00の間のすべての半分の1時間に1回トリガされます。」ただ、必ず文書のチェックではないすべてのオペレーティングシステムのcron式は、互換的に使用されているので、缶を。

私たちは、DIを追加していきますStartup.ConfigureServices()彼らのスケジュールを設定します。

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;

namespace QuartzHostedService
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            //添加Quartz服务
            services.AddSingleton<IJobFactory, SingletonJobFactory>();
            services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
            //添加我们的Job
            services.AddSingleton<HelloWorldJob>();
            services.AddSingleton(
                 new JobSchedule(jobType: typeof(HelloWorldJob), cronExpression: "0/5 * * * * ?")
           );
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           ......
        }
    }
}

このコードは、例DIコンテナに単一の4つのコンテンツに追加されます。

  • SingletonJobFactory 先に説明され、ジョブ・インスタンスを作成するために使用されます。
  • 一つのISchedulerFactory実装では、ビルトインを使用してStdSchedulerFactory、ジョブのスケジューリングと管理を処理することができます
  • HelloWorldJobジョブ自体
  • タイプHelloWorldJob、とcron式後5秒の実行を含んでJobScheduleインスタンス化するオブジェクト。

今、私たちは、基本的な作業のほとんどを完了していることを、ただ、それらを一緒に組み合わせて欠落していますQuartzHostedService

作成QuartzHostedService

これは、QuartzHostedServiceあるIHostedServiceセットクォーツスケジューラの実装、およびバックグラウンドで実行することを可能にします。クォーツのデザインなので、我々はできるIHostedService、直接ではなく、それを実装ベースからBackgroundService、より一般的な方法のクラスを派生します下記に記載されているサービスのための完全なコードは、私は後に詳述します。

using Microsoft.Extensions.Hosting;
using Quartz;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace QuartzHostedService
{
    public class QuartzHostedService : IHostedService
    {
        private readonly ISchedulerFactory _schedulerFactory;
        private readonly IJobFactory _jobFactory;
        private readonly IEnumerable<JobSchedule> _jobSchedules;

        public QuartzHostedService(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, IEnumerable<JobSchedule> jobSchedules)
        {
            _schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory));
            _jobFactory = jobFactory ?? throw new ArgumentNullException(nameof(jobFactory));
            _jobSchedules = jobSchedules ?? throw new ArgumentNullException(nameof(jobSchedules));
        }
        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);
                jobSchedule.JobStatu = JobStatus.Scheduling;
            }
            await Scheduler.Start(cancellationToken);
            foreach (var jobSchedule in _jobSchedules)
            {
                jobSchedule.JobStatu = JobStatus.Running;
            }
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            await Scheduler?.Shutdown(cancellationToken);
            foreach (var jobSchedule in _jobSchedules)
            {
             
                jobSchedule.JobStatu = JobStatus.Stopped;
            }
        }

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

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

QuartzHostedService3つの相互依存の依存性:私たちがしているStartupで設定ISchedulerFactoryしてIJobFactoryありますIEnumerable<JobSchedule>私たちは、DIコンテナに追加するJobScheduleオブジェクト(つまりHelloWorldJob)が、あなたはDIコンテナに計画され、より多くの作業のためにサインアップした場合、彼らはここにすべての注入(もちろん、あなたはまた、UIコントロールと相まって、データベースを介して取得することができます、それの視覚的背景のスケジューリングを達成することではないのですか?自分の想像力、それは〜)。

StartAsyncアプリケーションは、当社の構成クォーツの場所であるので、ここでは、起動時にメソッドが呼び出されます。最初のIScheduler後の使用のために割り当て属性、次いで注入の場合、JobFactoryディスパッチャに提供される実施例:

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

次に、我々ループ注入操作は、計画と使用は、各ジョブのクラスの最後で定義されたCreateJobCreateTriggerクォーツの方法の創出を支援IJobDetailしてITriggerあなたがこの作品の一部、または複数の制御を設定する必要が気に入らない場合は、必要に応じて、あなたが拡大することができJobSchedule、容易DTOそれをカスタマイズします。

public async Task StartAsync(CancellationToken cancellationToken)
{
    // ...
   foreach (var jobSchedule in _jobSchedules)
            {
                var job = CreateJob(jobSchedule);
                var trigger = CreateTrigger(jobSchedule);
                await Scheduler.ScheduleJob(job, trigger, cancellationToken);
                jobSchedule.JobStatu = JobStatus.Scheduling;
            }
    // ...
}

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

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

すべてのジョブがスケジュールされた後最後に、あなたはそれを呼び出すことができScheduler.Start()、バックグラウンドでの実際の開始Quartz.NET計画プロセスに対処します。アプリケーションが閉じているときに、フレームワークが呼び出されますStopAsync()、そして、あなたは呼び出すことができますScheduler.Stop()安全にするために、スケジューラプロセスをシャットダウンします。

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

あなたは使用することができますAddHostedService()マネージドサービスに拡張メソッドをStartup.ConfigureServices、当社のバックオフィスサービスへ:

public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddHostedService<QuartzHostedService>();
}

アプリケーションを実行する場合は、バックグラウンドタスクを実行すると、コンソール(または構成ロギングどこでも)を書くために5秒ごとに一度表示されるはずです

画像-20200406151153107

ジョブでサービススコープを使用します

そこには大きな問題を達成するために、この資料に記載されている:あなたが唯一のシングルトンまたは一時ジョブを作成することができます。この手段は、あなたは、任意のサービスの依存関係のスコープに対して登録を使用することはできません。たとえば、あなたはのEF中核ことはできませんDatabaseContextあなたの注入IJob、あなたが遭遇するので、実装をキャプティブ依存関係の問題を。

この問題を解決することは難しいことではありません:あなたは噴射することができるIServiceProviderし、独自のスコープを作成します。あなたがする必要がある場合たとえば、HelloWorldJobサービスの範囲を使用するには、次を使用することができます。

public class HelloWorldJob : IJob
{
    // 注入DI provider
    private readonly IServiceProvider _provider;
    public HelloWorldJob( IServiceProvider provider)
    {
        _provider = provider;
    }

    public Task Execute(IJobExecutionContext context)
    {
        // 创建一个新的作用域
        using(var scope = _provider.CreateScope())
        {
            // 解析你的作用域服务
            var service = scope.ServiceProvider.GetService<IScopedService>();
            _logger.LogInformation("Hello world by yilezhu at {0}!",DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }

        return Task.CompletedTask;
    }
}

あなたができるように、このことを保証するの両方が、ジョブを実行するたびに新しいスコープを作成するIJobサービスの範囲を取得(および対処します)。残念ながら、言葉遣いは確かにいくつかの混乱です。次回の記事で、私はあなたよりエレガントを達成するための別の方法を紹介し、より簡潔な、興味が「DotNetCoreの本当の」アップデートの公開数が初めてに従うことができます。

概要

この記事では、私はQuartz.NETを導入して、ASP.NETのコアでそれを使用する方法を示しIHostedServiceスケジューリングバックグラウンドジョブ。例は、この記事では、サービススコープの使用は非常に不器用なので、理想的ではない、最も適したシングルトンまたは過渡運転を示します。次回の記事で、私はあなたよりエレガントを達成するための別の方法を紹介し、それが「DotNetCoreの本当の」アップデートの公開数が初めてに従うことができます興味を持って、より簡潔で、それが簡単にサービススコープを使用することができます。

おすすめ

転載: www.cnblogs.com/yilezhu/p/12644208.html