Quartz.net分散タイミングタスクのポスチャを3分でマスター

はじめに

長い話を短くするために、今日の分散タイミングタスクについて話します。

  • ASP.NET Core + Quartz.NetはWebタイミングタスクを実装します
  • AspNetCoreとRedisプラクティスメッセージキューの組み合わせ

慎重に分析した結果、友人はまだ問題があることを知っています。
水平に拡張されたWebApp Quartz.netスケジュールタスクは複数回トリガーされます。
これは、webappインスタンスがデフォルトを使用し、RAMJobStore複数のインスタンスがジョブとトリガーのコピーをメモリに保持するためです。 。

私のスケジュールされたタスクは同期タスクです。複数回実行することは大きな問題ではありませんが、特定のビジネスのスケジュールされたタスクでは、複数の実行が致命的な問題になる場合があります。

これに基づいて、Quartz.net分散タイミングタスクのポーズを見てください。

AdoJobStore

明らかに、水平方向に拡張するマルチインスタンスには、ジョブとトリガーを保存するために、Webインスタンスとは独立したメカニズムが必要です。

Quartz.NETは、ジョブデータを格納するADO.NET JobStoreを提供します。

  1. 最初にSQLスクリプトを使用して、指定したテーブル構造をデータベースに生成します

スクリプトを実行すると、データベースにQRTZ_で始まるいくつかのテーブルが表示されます。

  1. AdoJobStoreを使用するようにQuartz.netを構成する

構成は、エンコーディングまたはquartz.configの形式で追加できます

簡単な練習

1.事前にジョブテーブルとトリガーテーブルを生成する

https://github.com/quartznet/quartznet/tree/master/database/tablesから適切なデータベーステーブルスクリプトをダウンロードして、指定されたテーブル構造を生成します

2. AdoJobStoreを追加します

今回は、AdoJobStore構成がエンコーディングを使用して追加されます。
最初の起動では、ジョブとトリガーをコード内でsqliteに永続化し、ジョブとトリガーをsqliteから直接ロードします

using System;
using System.Collections.Specialized;
using System.Data;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.AdoJobStore.Common;
using Quartz.Spi;

namespace EqidManager
{
    using IOCContainer = IServiceProvider;

    public class QuartzStartup
    {
        public IScheduler Scheduler { get; set; }

        private readonly ILogger _logger;
        private readonly IJobFactory iocJobfactory;
        public QuartzStartup(IOCContainer IocContainer, ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<QuartzStartup>();
            iocJobfactory = new IOCJobFactory(IocContainer);

            DbProvider.RegisterDbMetadata("sqlite-custom", new DbMetadata()
            {
                AssemblyName = typeof(SqliteConnection).Assembly.GetName().Name,
                ConnectionType = typeof(SqliteConnection),
                CommandType = typeof(SqliteCommand),
                ParameterType = typeof(SqliteParameter),
                ParameterDbType = typeof(DbType),
                ParameterDbTypePropertyName = "DbType",
                ParameterNamePrefix = "@",
                ExceptionType = typeof(SqliteException),
                BindByName = true
            });

            var properties = new NameValueCollection
            {
                ["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
                ["quartz.jobStore.useProperties"] = "true",
                ["quartz.jobStore.dataSource"] = "default",
                ["quartz.jobStore.tablePrefix"] = "QRTZ_",
                ["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SQLiteDelegate, Quartz",
                ["quartz.dataSource.default.provider"] = "sqlite-custom",
                ["quartz.dataSource.default.connectionString"] = "Data Source=EqidManager.db",
                ["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz",
                ["quartz.serializer.type"] = "binary"
            };

            var schedulerFactory = new StdSchedulerFactory(properties);
            Scheduler = schedulerFactory.GetScheduler().Result;
            Scheduler.JobFactory = iocJobfactory;
        }

        public async Task<IScheduler> ScheduleJob()
        {
            var _eqidCounterResetJob = JobBuilder.Create<EqidCounterResetJob>()
              .WithIdentity("EqidCounterResetJob")
              .Build();

            var _eqidCounterResetJobTrigger = TriggerBuilder.Create()
                .WithIdentity("EqidCounterResetCron")
                .StartNow()                          
                //每天凌晨0s
                .WithCronSchedule("0 0 0 * * ?")      Seconds,Minutes,Hours,Day-of-Month,Month,Day-of-Week,Year(optional field)
                .Build();
         
           // 这里一定要先判断是否已经从SQlite中加载了Job和Trigger
            if (!await Scheduler.CheckExists(new JobKey("EqidCounterResetJob")) && 
                !await Scheduler.CheckExists(new TriggerKey("EqidCounterResetCron")))
            {
                await Scheduler.ScheduleJob(_eqidCounterResetJob, _eqidCounterResetJobTrigger);
            }
            
            await Scheduler.Start();
            return Scheduler;
        }

        public void EndScheduler()
        {
            if (Scheduler == null)
            {
                return;
            }

            if (Scheduler.Shutdown(waitForJobsToComplete: true).Wait(30000))
                Scheduler = null;
            else
            {
            }
            _logger.LogError("Schedule job upload as application stopped");
        }
    }
}

上記はsqliteからQuartz.NETロードジョブとトリガーのコアコードです

2つのヒントを次に示します。

①。IOCJobFactoryはカスタムJobFactoryであり、目的はASP.NET Coreネイティブ依存性注入と組み合わせることです
②。タスクをスケジュールするとき、最初にジョブとトリガーがsqliteからロードされているかどうかを判断する必要があります

3. Quartz.Net UIホイールを追加する

Quartz.NETのスケジューリングUI:CrystalQuartzが付属しており、インターフェースでのタスクのスケジューリングを容易にします
①インストールパッケージCrystalQuartz.AspNetCore -IncludePrerelease②
スタートアップによりCrystalQuartzが有効になります

using CrystalQuartz.AspNetCore;
/*
 * app is IAppBuilder
 * scheduler is your IScheduler (local or remote)
 */
var quartz = app.ApplicationServices.GetRequiredService<QuartzStartup>();
var _schedule = await  quartz.ScheduleJob();
app.UseCrystalQuartz(() => scheduler);

③localhost:YOUR_PORT /クォーツアドレスでスケジュールを確認します

要約出力

  1. Quartz.netはAdoJobStoreを使用して分散タイミングタスクをサポートし、複数のインスタンスが複数回トリガーされる問題を解決します
  2. ホイールをすばやく投げる:Quartz.Net UIライブラリ

おすすめ

転載: www.cnblogs.com/JulianHuang/p/12720436.html