ASP.NET Core 2.2+Quartz.Net 實現Web定時任務

作者:Julian_醬

鏈接:http://www.cnblogs.com/mi12205599/p/10361763.html


作為一枚后端程序狗,項目實踐常遇到定時任務的工作,最容易想到的的思路就是利用Windows計劃任務/wndows service程序/Crontab程序等主機方法在主機上部署定時任務程序/腳本。


但是很多時候,若使用的是共享主機或者受控主機,這些主機不允許你私自安裝exe程序、Windows服務程序。


碼甲會想到在web程序中做定時任務, 目前有兩個方向:


1、AspNetCore自帶的HostService, 這是一個輕量級的后臺服務, 需要搭配timer完成定時任務


2、老牌Quartz.Net組件,支持復雜靈活的Scheduling、支持ADO/RAM Job任務存儲、支持集群、支持監聽、支持插件。


此處我們的項目使用稍復雜的Quartz.net實現Web定時任務。

項目背景

最近需要做一個計數程序:采用redis計數,設定每小時將當日累積數據持久化到關系型數據庫sqlite。


添加Quartz.Net Nuget 依賴包:<PackageReference Include="Quartz" Version="3.0.6" />


1、定義定時任務內容: Job

2、設置觸發條件: Trigger

3、將Quartz.Net集成進AspNet Core

頭腦風暴

IScheduler類包裝了上述背景需要完成的第①②點工作 ,SimpleJobFactory定義了生成指定的Job任務的過程,這個行為是利用反射機制調用無參構造函數構造出的Job實例。


下面是源碼:


//----------------選自Quartz.Simpl.SimpleJobFactory類-----------
using System;
using Quartz.Logging;
using Quartz.Spi;
using Quartz.Util;
namespace Quartz.Simpl
{
? ?/// <summary>
? ?/// The default JobFactory used by Quartz - simply calls
? ?/// <see cref="ObjectUtils.InstantiateType{T}" /> on the job class.
? ?/// </summary>
? ?/// <seealso cref="IJobFactory" />
? ?/// <seealso cref="PropertySettingJobFactory" />
? ?/// <author>James House</author>
? ?/// <author>Marko Lahma (.NET)</author>
? ?public class SimpleJobFactory : IJobFactory
? ?{
? ? ? ?private static readonly ILog log = LogProvider.GetLogger(typeof (SimpleJobFactory));
? ? ? ?/// <summary>
? ? ? ?/// Called by the scheduler at the time of the trigger firing, in order to
? ? ? ?/// produce a <see cref="IJob" /> instance on which to call Execute.
? ? ? ?/// </summary>
? ? ? ?/// <remarks>
? ? ? ?/// It should be extremely rare for this method to throw an exception -
? ? ? ?/// basically only the case where there is no way at all to instantiate
? ? ? ?/// and prepare the Job for execution. ?When the exception is thrown, the
? ? ? ?/// Scheduler will move all triggers associated with the Job into the
? ? ? ?/// <see cref="TriggerState.Error" /> state, which will require human
? ? ? ?/// intervention (e.g. an application restart after fixing whatever
? ? ? ?/// configuration problem led to the issue with instantiating the Job).
? ? ? ?/// </remarks>
? ? ? ?/// <param name="bundle">The TriggerFiredBundle from which the <see cref="IJobDetail" />
? ? ? ?/// ? and other info relating to the trigger firing can be obtained.</param>
? ? ? ?/// <param name="scheduler"></param>
? ? ? ?/// <returns>the newly instantiated Job</returns>
? ? ? ?/// <throws> ?SchedulerException if there is a problem instantiating the Job. </throws>
? ? ? ?public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
? ? ? ?
{
? ? ? ? ? ?IJobDetail jobDetail = bundle.JobDetail;
? ? ? ? ? ?Type jobType = jobDetail.JobType;
? ? ? ? ? ?try
? ? ? ? ? ?{
? ? ? ? ? ? ? ?if (log.IsDebugEnabled())
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?log.Debug($"Producing instance of Job '{jobDetail.Key}', class={jobType.FullName}");
? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?return ObjectUtils.InstantiateType<IJob>(jobType);
? ? ? ? ? ?}
? ? ? ? ? ?catch (Exception e)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?SchedulerException se = new SchedulerException($"Problem instantiating class '{jobDetail.JobType.FullName}'", e);
? ? ? ? ? ? ? ?throw se;
? ? ? ? ? ?}
? ? ? ?}

? ? ? ?/// <summary>
? ? ? ?/// Allows the job factory to destroy/cleanup the job if needed.
? ? ? ?/// No-op when using SimpleJobFactory.
? ? ? ?/// </summary>
? ? ? ?public virtual void ReturnJob(IJob job)
? ? ? ?
{
? ? ? ? ? ?var disposable = job as IDisposable;
? ? ? ? ? ?disposable?.Dispose();
? ? ? ?}
? ?}
}


//------------------節選自Quartz.Util.ObjectUtils類------------
public static T InstantiateType<T>(Type type)
{
? ? if(type == null)
? ? {
? ? ? ? ?throw new ArgumentNullException(nameof(type), "Cannot instantiate null");
? ? }
? ? ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
? ? if (ci == null)
? ? {
? ? ? ? ?throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);
? ? }
? ? return (T) ci.Invoke(new object[0]);
}


很多時候,定義的Job任務依賴了其他組件(Job實例化時多參),此時默認的SimpleJobFactory不能滿足實例化要求, 需要考慮將Job任務作為依賴注入組件,加入依賴注入容器。


關鍵思路:


① IScheduler 開放了JobFactory 屬性,便于你控制Job任務的實例化方式;


JobFactories may be of use to those wishing to have their application produce IJob instances via some special mechanism, such as to give the opportunity for dependency injection


②AspNet Core的服務架構是以依賴注入為基礎的,利用ASPNET Core已有的依賴注入容器IServiceProvider管理Job任務的創建過程。

編碼實踐

1、定義Job內容

// -------每小時將redis數據持久化到sqlite, 每日凌晨跳針,持久化昨天全天數據-
public class UsageCounterSyncJob : IJob
{
? ? ? ?private readonly EqidDbContext _context;
? ? ? ?private readonly IDatabase _redisDB1;
? ? ? ?private readonly ILogger _logger;
? ? ? ?public UsageCounterSyncJob(EqidDbContext context, RedisDatabase redisCache, ILoggerFactory loggerFactory)
? ? ? ?
{
? ? ? ? ? ?_context = context;
? ? ? ? ? ?_redisDB1 = redisCache[1];
? ? ? ? ? ?_logger = loggerFactory.CreateLogger<UsageCounterSyncJob>();
? ? ? ?}
? ? ? ?public async Task Execute(IJobExecutionContext context)
? ? ? ?
{
? ? ? ? ? ?// 觸發時間在凌晨,則同步昨天的計數
? ? ? ? ? ?var _day = DateTime.Now.ToString("yyyyMMdd");
? ? ? ? ? ?if (context.FireTimeUtc.LocalDateTime.Hour == 0)
? ? ? ? ? ? ? ?_day = DateTime.Now.AddDays(-1).ToString("yyyyMMdd");

? ? ? ? ? ?await SyncRedisCounter(_day);
? ? ? ? ? ?_logger.LogInformation("[UsageCounterSyncJob] Schedule job executed.");

? ? ? ?}
? ? ? ?......
}


2、注冊Job和Trigger

namespace EqidManager
{
? ?using IOCContainer = IServiceProvider;
? ?// Quartz.Net啟動后注冊job和trigger
? ?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);
? ? ? ? ? ?var schedulerFactory = new StdSchedulerFactory();
? ? ? ? ? ?_scheduler = schedulerFactory.GetScheduler().Result;
? ? ? ? ? ?_scheduler.JobFactory = iocJobfactory;
? ? ? ?}
? ? ? ?public void Start()
? ? ? ?
{
? ? ? ? ? ?_logger.LogInformation("Schedule job load as application start.");
? ? ? ? ? ?_scheduler.Start().Wait();
? ? ? ? ? ?var UsageCounterSyncJob = JobBuilder.Create<UsageCounterSyncJob>()
? ? ? ? ? ? ? .WithIdentity("UsageCounterSyncJob")
? ? ? ? ? ? ? .Build();
? ? ? ? ? ?var UsageCounterSyncJobTrigger = TriggerBuilder.Create()
? ? ? ? ? ? ? ?.WithIdentity("UsageCounterSyncCron")
? ? ? ? ? ? ? ?.StartNow()
? ? ? ? ? ? ? ?// 每隔一小時同步一次
? ? ? ? ? ? ? ?.WithCronSchedule("0 0 * * * ?") ? ? ?// Seconds,Minutes,Hours,Day-of-Month,Month,Day-of-Week,Year(optional field)
? ? ? ? ? ? ? ?.Build();
? ? ? ? ? ?_scheduler.ScheduleJob(UsageCounterSyncJob, UsageCounterSyncJobTrigger).Wait();
? ? ? ? ? ?_scheduler.TriggerJob(new JobKey("UsageCounterSyncJob"));
? ? ? ?}
? ? ? ?public void Stop()
? ? ? ?
{
? ? ? ? ? ?if (_scheduler == null)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?return;
? ? ? ? ? ?}
? ? ? ? ? ?if (_scheduler.Shutdown(waitForJobsToComplete: true).Wait(30000))
? ? ? ? ? ? ? ?_scheduler = null;
? ? ? ? ? ?else
? ? ? ? ? ?{
? ? ? ? ? ?}
? ? ? ? ? ?_logger.LogCritical("Schedule job upload as application stopped");
? ? ? ?}
? ?}
? ?/// <summary>
? ?/// IOCJobFactory :實現在Timer觸發的時候注入生成對應的Job組件
? ?/// </summary>
? ?public class IOCJobFactory : IJobFactory
? ?{
? ? ? ?protected readonly IOCContainer Container;
? ? ? ?public IOCJobFactory(IOCContainer container)
? ? ? ?
{
? ? ? ? ? ?Container = container;
? ? ? ?}
? ? ? ?//Called by the scheduler at the time of the trigger firing, in order to produce
? ? ? ?//a Quartz.IJob instance on which to call Execute.
? ? ? ?public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
? ? ? ?
{
? ? ? ? ? ?return Container.GetService(bundle.JobDetail.JobType) as IJob;
? ? ? ?}
? ? ? ?// Allows the job factory to destroy/cleanup the job if needed.
? ? ? ?public void ReturnJob(IJob job)
? ? ? ?
{
? ? ? ?}
? ?}
}


3、結合ASpNet Core 注入組件;綁定Quartz.Net

//-------------------------------截取自Startup文件---------------
......
services.AddTransient<UsageCounterSyncJob>(); ? ?
// 這里使用瞬時依賴注入
services.AddSingleton<QuartzStartup>();
......
// 綁定Quartz.Net
public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IApplicationLifetime lifetime, ILoggerFactory loggerFactory)
{
? ? var quartz = app.ApplicationServices.GetRequiredService<QuartzStartup>();
? ? lifetime.ApplicationStarted.Register(quartz.Start);
? ? lifetime.ApplicationStopped.Register(quartz.Stop);
}


附:IIS 網站低頻訪問導致工作進程進入閑置狀態的 解決辦法


IIS為網站默認設定了20min閑置超時時間:20分鐘內沒有處理請求、也沒有收到新的請求,工作進程就進入閑置狀態。


IIS上低頻web訪問會造成工作進程關閉,此時應用程序池回收,Timer等線程資源會被銷毀;當工作進程重新運作,Timer可能會重新生成起效, 但我們的設定的定時Job可能沒有按需正確執行。


640?wx_fmt=png


故為在IIS網站實現低頻web訪問下的定時任務:


設置Idle TimeOut =0;同時將【應用程序池】->【正在回收】->不勾選【回收條件】? ? ??


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272165.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272165.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272165.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

lj245a引腳功能圖_ULN2003A引腳圖及功能-uln2003a原理

ULN是集成達林頓管IC&#xff0c;內部還集成了一個消線圈反電動勢的二極管&#xff0c;可用來驅動繼電器。它是雙列16腳封裝,NPN晶體管矩陣,最大驅動電壓50V,電流500mA,輸入電壓5V,適用于TTL COMS,由達林頓管組成驅動電路。ULN是集成達林頓管IC,內部還集成了一個消線圈反電動勢…

RocketMQ核心概念

生產者Producer和消費者Consumer NameServer作用 Broker和Topic

交叉編譯、軟硬鏈接

什么是交叉編譯&#xff1f;交叉編譯是一個行為&#xff0c;是在一個平臺上生成另一個平臺上的可執行代碼。 本地編譯&#xff1a;本地編譯可以理解為&#xff0c;在當前編譯平臺下&#xff0c;編譯出來的程序只能放到當前平臺下運行。平時我們常見的軟件開發&#xff0c;都是…

掃地機器人狗毛_掃地機器人:我是清理狗毛的!不是清理狗屎的!

原標題&#xff1a;掃地機器人&#xff1a;我是清理狗毛的&#xff01;不是清理狗屎的&#xff01;掃地機器人可以清潔地面和角落里的垃圾&#xff0c;對于滿是毛毛的鏟屎官家庭來說&#xff0c;簡直就是福音吶&#xff01;不過最近&#xff0c;槽點卻有點多&#xff1a;家里買…

Linus下安裝maven

下載maven安裝包 wget http://mirror.bit.edu.cn/apache/maven/binaries/apache-maven-3.2.2-bin.tar.gz 解壓 tar -zxvf apache-maven-3.2.2-bin.tar.gz 配置maven環境變量 查看maven解壓后安裝包目錄 vi /etc/profile 進入最底部&#xff0c;按insert,添加環境變量&#x…

linux內核開發基礎(linux內核源碼、樹莓派源碼編譯、SD卡掛載)

首先下載樹莓派linux內核源碼&#xff1a; 下載網址&#xff1a;https://github.com/raspberrypi/linux在樹莓派使用指令&#xff1a;uname -r查看當前樹莓派的版本號&#xff0c;然后選擇對應的linux內核版本號進行下載。 將linux內核源碼從共享文件夾拷貝到SYSTEM文件夾&am…

Linux實時查看進程命令top筆記

top命令是Linux下常用的性能分析工具&#xff0c;能夠實時顯示Linux系統中各個進程的資源占用狀況&#xff0c;類似于Windows系統的任務管理器功能。 top命令的語法格式&#xff1a; top [-] [d] [p] [q] [c] [C] [S] [s] [n] 常用參數說明 d 指定每兩次屏幕信息刷新之間的時間…

C#基礎之Equals和Dispose

1.equal()和運算符的區別 由于C#中有值類型和引用類型&#xff0c;那么相等也分為值相等和引用相等。先來看一個值類型簡單的例子&#xff0c;順便也寫了string類型的比較。 static void Main(string[] args){int n1 1;int n2 1;Console.WriteLine(n1n2);Console.WriteLine(n…

mysql 時間chuo格式化_Mysql時間戳與時間格式轉換問題匯總

一、時間戳的定義時間戳指格林威治時間1970年01月01日00時00分00秒起至現在的總秒數。二、時間格式轉時間戳的方法使用unix_timestamp函數&#xff0c;如下&#xff1a;SELECT UNIX_TIMESTAMP();//返回當前時間戳SELECT UNIX_TIMESTAMP(2017-12-16 17:29:56) AS t; //返回指定時…

文件系統(文件系統目錄結構、磁盤分區、虛擬文件系統)、linux內核結構框圖

什么是文件系統&#xff1f; 常規認知就是根目錄下那些文件&#xff0c;但其實并不是那樣。文件系統是操作系統用于明確存儲設備&#xff08;常見的是磁盤&#xff0c;也有基于NAND Flash的固態硬盤&#xff09;或分區上的文件的方法和數據結構&#xff1b;即在存儲設備上組織…

dockerq啟動報錯(iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --t

docker啟動報錯 (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --to-destination 172.17.0.2:9876 ! -i docker0: iptables: No chain/target/match by that name. 解決方案&#xff1a;重啟docker systemctl restart docker

Linux進程終止命令kill或kill all?筆記

在linux命令下&#xff0c;如果需要終止某個進程&#xff0c;可以使用kill或者killall等命令來實現。終止命令的原理都是向linux內核發送一個系統操作的信號以及某個進程的ID&#xff0c;然后系統內核會根據指定的進程ID進行相應的處理。 kill命令典型的用法&#xff1a;首先使…

linux驅動(驅動編譯、字符設備驅動框架、交叉編譯樹莓派驅動、樹莓派驅動本地編譯)

什么是驅動&#xff1a; 驅動就是對底層硬件設備的操作進行封裝&#xff0c;并向上層提供函數接口。 設備分類&#xff1a; linux系統將設備分為3類&#xff1a;字符設備、塊設備、網絡設備。 字符設備&#xff1a;指只能一個字節一個字節讀寫的設備&#xff0c;不能隨機讀取…

docker啟動報錯 ?(iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --

docker啟動報錯 : (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --to-destination 172.17.0.2:9876 ! -i docker0: iptables: No chain/target/match by that name. 解決方案&#xff1a; systemctl restart docker

第一個Spark程序

1、Java下Spark開發環境搭建&#xff08;from http://www.cnblogs.com/eczhou/p/5216918.html&#xff09;1.1、jdk安裝安裝oracle下的jdk&#xff0c;我安裝的是jdk 1.7&#xff0c;安裝完新建系統環境變量JAVA_HOME&#xff0c;變量值為“C:\Program Files\Java\jdk1.7.0_79”…

arduino判斷是否連接串口_Arduino-串口通信

Serial”系列函數&#xff0c;所以我們要對其有所了解&#xff0c;下面介紹幾個常“Serial”函數。1、Serial.begin()—設置串行每秒傳輸數據的速率(波特率)。在同計算機通訊時&#xff0c;使用下面這些值&#xff1a;300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400…

最詳細的docker安裝rocketMQ教程來了

RocketMQ是一款分布式、隊列模型的消息中間件&#xff0c;是由阿里巴巴設計的&#xff0c;具有以下特點&#xff1a; 支持嚴格的消息順序 支持Topic與Queue兩種模式 億級消息堆積能力 比較友好的分布式特性 同時支持Push與Pull方式消費消息 歷經多次天貓雙十一海量消息考驗…

樹莓派IO口驅動代碼的編寫、微機總線地址、物理地址、虛擬地址、BCM2835芯片手冊

地址總線&#xff1a; 百度百科解釋&#xff1a; 地址總線 (Address Bus&#xff1b;又稱&#xff1a;位址總線) 屬于一種電腦總線 &#xff08;一部份&#xff09;&#xff0c;是由CPU 或有DMA 能力的單元&#xff0c;用來溝通這些單元想要存取&#xff08;讀取/寫入&#xff…

奪命雷公狗---DEDECMS----26dedecms面包屑導航的實現

我們在很多項目里面都會用到面包屑導航&#xff0c;而dedecms里面也是給我們封裝好面包屑導航的了,如下圖所示&#xff1a; 在dede里面實現面包屑導航主要用到{dede:field.position/}標簽&#xff0c;我們首先來修改下article_movie.htm內容頁的模版文件&#xff1a; 我們修改成…

rust油桶用什么打_草莓用什么膨大素好?草莓膨大劑什么時間打?草莓用什么肥料膨大...

農資365公眾號&#xff0c;了解更多生根、根腐、重茬、土傳、枯黃萎、根爛病、防治根結線蟲、微生物菌肥、膨大坐果、抗病增產的防治方法&#xff01;草莓含有豐富的營養&#xff0c;并且種植效益較高&#xff0c;其種植范圍也比較廣。草莓種植期間有很多因素影響草莓果實膨大&…