关山难越,谁悲失路之人;萍水相逢,尽是他乡之客。
百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程教程 > 后端模块 > 正文

ASP.NET Core+Quartz.Net实现web定时任务

guanshanw 2023-05-28 18:36 154 浏览 2 评论

加个“星标”,每天清晨 07:25,干货推送!

本文转载自公众号【Dotnet Plus】——作者【小码甲】

ASP.NET Core+Quartz.Net实现web定时任务

作为一枚后端程序狗,项目实践常遇到定时任务的工作,最容易想到的的思路就是利用Windows计划任务/wndows service程序/Crontab程序等主机方法在主机上部署定时任务程序/脚本。

但是很多时候,使用的是共享主机或者受控主机,这些主机不允许你私自安装exe程序、Windows服务程序。

web程序中做定时任务,目前有两个方向:

① ASP.NET Core自带的HostService, 这是一个轻量级的后台服务,需要搭配timer完成定时任务②老牌Quartz.Net组件,支持复杂灵活的Scheduling、支持ADO/RAM Job任务存储、支持集群、支持监听、支持插件。

此处我们的项目使用稍复杂的Quartz.net实现web定时任务。

项目背景

最近需要做一个计数程序:采用redis计数,设定每小时将当日累积数据持久化到关系型数据库sqlite。

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

① 定义定时任务内容:Job

② 设置触发条件:Trigger

③ 将Quartz.Net集成进ASP.NET 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>
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>

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 == )
{
throw new ArgumentException(nameof(type), "Cannot instantiate ");
}
ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
if (ci == )
{
throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);
}
return (T) ci.Invoke(new object[0]);
}

很多时候,定义的Job任务依赖了其他服务(该Job定义有参构造函数),此时默认的SimpleJobFactory不能满足实例化要求,考虑自定义Job工厂类

关键思路:① Quartz.Net提供IJobFactory接口,以便开发者定义灵活的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

② ASP.NET Core是以依赖注入为基础的,利用ASP.NET Core内置依赖注入容器IServiceProvider管理Job的实例化依赖

编码实践

已经定义好Job类:UsageCounterSyncJob

  1. 自定义Job工厂类:IOCJobFactory

 /// <summary>
/// IOCJobFactory :在Timer触发的时候产生对应Job实例
/// </summary>
public class IOCJobFactory : IJobFactory
{
protected readonly IServiceProvider Container;

public IOCJobFactory(IServiceProvider 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)
{
}
}
  1. 在Quartz启动过程中应用自定义Job工厂

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

private readonly ILogger _logger;
private readonly IJobFactory iocJobfactory;
public QuartzStartup(IServiceProvider IocContainer, ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<QuartzStartup>;
iocJobfactory = new IOCJobFactory(IocContainer);
var schedulerFactory = new StdSchedulerFactory;
_scheduler = schedulerFactory.GetScheduler.Result;
_scheduler.JobFactory = iocJobfactory;
}
// Quartz.Net启动后注册job和trigger
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 == )
{
return;
}
if (_scheduler.Shutdown(waitForJobsToComplete: true).Wait(30000))
_scheduler = ;
else
{
}
_logger.LogCritical("Schedule job upload as application stopped");
}
}
  1. 结合ASP.NET Core依赖注入;web启动时绑定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);
}

以上: 我们对接ASP.NET Core依赖注入框架实现了一个自定义的JobFactory,每次任务触发时创建瞬时Job.

Github地址:https://github.com/zaozaoniao/ASPNETCore-Quartz.NET.git

附:IIS网站低频访问导致工作进程进入闲置状态的解决办法

IIS为网站默认设定了20min闲置超时时间:20分钟内没有处理请求、也没有收到新的请求,工作进程就进入闲置状态。

IIS上低频web访问会造成工作进程关闭,此时应用程序池回收,Timer等线程资源会被销毁;当工作进程重新运作,Timer可能会重新生成, 但我们的设定的定时Job可能没有按需正确执行。

故为IIS站点实现低频web访问下的定时任务:

相关推荐

说一说servlet的生命周期以及基本架构

说一说Servlet的生命周期?答:servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,servi...

JAVA WEB教程之一(何为servlet)

JavaServlet是运行在Web服务器或应用服务器上的程序,它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。Servlet...

Servlet深入解读,有你想知道的!

什么是servlet?Servlet是基于Java技术的web组件,容器托管的,用于生成动态内容。像其他基于Java的组件技术一样,Servlet也是基于平台无关的Java类格式,被编译为平台无关的字...

浅谈Servlet

JavaServlet是一种运行在服务器端的Java程序,用于接收和响应来自客户端的HTTP请求。Servlet通常被用于开发Web应用程序,能够动态产生网页内容,与客户端交互,处理表单的输入和数据...

消息复杂计算的抽象和简化

作者:蒋文豪(四点)消息客户端计算的复杂性在客户端的设计中,一般的分层会至少包含下层的数据服务层和上层的UI层,下层的数据模型主要由所在领域决定,相对独立、稳定,而UI则更多变,且会对多种数据进行组合...

大型互联网技术架构分析之分布式存储架构解析

前言在这个存储系统中包含很多组件,除了核心的机头(控制器)、磁盘阵列(JBOD)和交换机等设备外,还有管理设备等辅助设备一、集中存储结构说到分布式存储,我们先来看一下传统的存储是怎么个样子。传统的...

C++成为TIOBE 2022年度编程语言!

2022年12月的榜单中预测了C++、Python、C三门语言当中会有一个成为年度编程语言。如今C++脱颖而出成为了TIOBE2022年度编程语言的最终获得者!近段时间来,C++的发展有目共睹,可以...

最好的编程语言

现在每个人都在学习编写软件。这意味着每一所学校、MOOC和培训网站都需要为年轻的学徒们接受第一语言。有些地方,如腐朽的哈佛,仍然依附于70年代C期但许多学校在JavaScript之间摇摆不定,Pyth...

Swift 势必取代 Python?

源:http://985.so/bqzPSwift拥有强力的后援Swift是由ChrisLattner在苹果工作时创建的。目前,ChrisLattner在GoogleBrain工作——这是世界上...

儿童学编程语言swift语言 playgrounds21 席卷四处

通过我们之前所学的循环,我们知道,使用循环,最重要的要找出重复的规律。席卷四处在现在这个例子中,我们通过仔细观察就能发现,我们可以把靠近Byte的四颗宝石作为一组,其他的宝石都可以按这个进行重复。那么...

Swift:条件编译

应用场景在项目工程中编写代码,但是需要对部分功能做区分处理(系统、设备等)。就需要使用系统的条件编译方式来处理。API及语言Swift核心逻辑/代码主要是通过#ifxx#elseifxxx#...

Swift Playgrounds学习笔记补充(三)-函数:挑战关1、2

从现在开始,我感受到了编程的乐趣了:它没有一个正确的解决办法,只有更好的解决办法。挑战关1:搜集、切换和重复在这个挑战关里,首先先确定我们的目标:拿到4颗宝石和切换4个开关。然后再观察地图,我们的地图...

Swift Playgrounds学习笔记补充(四)-函数

大家好,今天是学习SwiftPlaygrounds的第四节课。我会尽可能详细地把我学到的内容分享给大家!请大家多多点赞关注,给我动力!合理使用函数的嵌套,会使代码层次分明,在后期的维护上会轻松许多。...

函数式编程之Map&amp;Reduce

函数式编程之Map&Reduce在上一篇中介绍了什么是函数式编程,以及介绍了最常用的一个函数式编程函数Filter详情请查看《Swift教程——函数式编程》这篇文章中介绍函数式编程中另外两...

RxSwift实现MVVM高仿喜马拉雅的函数响应式编程

源码下载https://github.com/sessionCh/RxXMLY注意事项1.源码下载后,执行podupdate–no-repo-update更新第三方库;2.项目运行中,如果数据...

已有2位网友发表了看法:

取消回复欢迎 发表评论: