1、介绍:Quartz.Net主要是用来做一些周期性的工作,或者定时工作。比如每天凌晨2点执行某个方法或者调用某个接口。

Quartz项目地址:https://github.com/quartz-scheduler/quartz

参考https://www.cnblogs.com/yilezhu/p/12644208.html

.Net Core可以通过HostedService很好的实现后台任务,HostedService跟随.Net Core应用启动而在后台启动。创建一个Quartz.NET的HostedService可以使用Quartz

Quartz可以通过其Cron表达式实现复杂定时规则的定时任务。

Quartz.NET有两个主要概念:

  • Job。这是您要按某个特定时间表运行的后台任务。
  • Scheduler。这是负责基于触发器,基于时间的计划运行作业

2、在NuGet 包管理器中安装,搜索Quartz 安装

3、创建一个IJob

我们将通过向注入的ILogger<>中写入“ hello world”来进行实现进而向控制台输出结果)。您必须实现包含单个异步Execute()方法的Quartz接口IJob。请注意,这里我们使用依赖注入将日志记录器注入到构造函数中。

    /// <summary>
/// 该属性可防止Quartz.NET尝试同时运行同一作业。告诉Quartz不要同时执行给定Job定义(引用给定Job类)的多个实例
/// </summary>
[DisallowConcurrentExecution]
public class SignalRJob : IJob
{
private readonly ILogger<SignalRJob> _logger;
public SignalRJob(ILogger<SignalRJob> logger)
{
_logger = logger;
} public Task Execute(IJobExecutionContext context)
{
_logger.LogWarning("Hello world!");
return Task.CompletedTask;
}
}

4.创建一个IJobFactory

默认情况下,Quartz将使用Activator.CreateInstance创建作业实例,从而有效的调用new HelloWorldJob()。不幸的是,由于我们使用构造函数注入,因此无法正常工作。相反,我们可以提供一个自定义的IJobFactory挂钩到ASP.NET Core依赖项注入容器(IServiceProvider)中:

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()方法。在这个方法中工厂必须返回Quartz调度程序所请求的IJob。在此实现中,我们直接委托给IServiceProvider,并让DI容器找到所需的实例。由于GetRequiredService的非泛型版本返回的是一个对象,因此我们必须在末尾将其强制转换成IJob。

该ReturnJob方法是调度程序尝试返回(即销毁)工厂创建的作业的地方。不幸的是,使用内置的IServiceProvider没有这样做的机制。我们无法创建适合Quartz API所需的新的IScopeService,因此我们只能创建单例作业。

这个很重要。使用上述实现,仅对创建单例(或瞬态)的IJob实现是安全的。

5.配置作业

我在IJob这里仅显示一个实现,但是我们希望Quartz托管服务是适用于任何数量作业的通用实现。为了解决这个问题,我们创建了一个简单的DTO JobSchedule,用于定义给定作业类型的计时器计划:

/// <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表达。Cron表达式允许复杂的计时器调度,因此您可以设置下面复杂的规则,例如“每月5号和20号在上午8点至10点之间每半小时触发一次”。只需确保检查文档即可,因为并非所有操作系统所使用的Cron表达式都是可以互换的。我们将作业添加到DI并在Startup.ConfigureServices()中配置其时间表

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容器:

SingletonJobFactory 是前面介绍的,用于创建作业实例。
一个ISchedulerFactory的实现,使用内置的StdSchedulerFactory,它可以处理调度和管理作业
该HelloWorldJob作业本身
一个类型为HelloWorldJob,并包含一个五秒钟运行一次的Cron表达式的JobSchedule的实例化对象。
现在我们已经完成了大部分基础工作,只缺少一个将他们组合在一起的、QuartzHostedService了。

6.创建QuartzHostedService

该QuartzHostedService是IHostedService的一个实现,设置了Quartz调度程序,并且启用它并在后台运行。由于Quartz的设计,我们可以在IHostedService中直接实现它,而不是从基BackgroundService类派生更常见的方法。该服务的完整代码在下面列出,稍后我将对其进行详细描述。

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();
}
}

该QuartzHostedService有三个依存依赖项:我们在Startup中配置的ISchedulerFactory和IJobFactory,还有一个就是IEnumerable<JobSchedule>。我们仅向DI容器中添加了一个JobSchedule对象(即HelloWorldJob),但是如果您在DI容器中注册更多的工作计划,它们将全部注入此处(当然,你也可以通过数据库来进行获取,再加以UI控制,是不是就实现了一个可视化的后台调度了呢?自己想象吧~)。

您可以使用AddHostedService()扩展方法在托管服务Startup.ConfigureServices中注入我们的后台服务:

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

7.在作业中使用作用域服务

这篇文章中描述的实现存在一个大问题:您只能创建Singleton或Transient作业。这意味着您不能使用注册为作用域服务的任何依赖项。例如,您将无法将EF Core的 DatabaseContext注入您的IJob实现中,因为您会遇到Captive Dependency问题。

解决这个问题也不是很难:您可以注入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实战”公众号第一时间获取更新。

考虑到IIS服务器的回收机制,需要设置一下IIS回收

 在IIS中找到这个站点所用的程序池,点击“高级设置...” 在打开的列表中更改以下设置:
 回收——固定时间间隔(分钟) 改为 0         
        ——虚拟/专用内存限制(KB) 改为 0 
  进程模型——闲置超时(分钟) 改为 0
  这样子,一般情况下程序池就不会被自动回收了,后台一些简单的计算线程就会正常工作

Asp.net Core使用Quartz.net的更多相关文章

  1. ASP.NET Core使用Quartz定时调度

    在应用程序开发过程中,经常会需要定时任务调度功能,本篇博客介绍Asp.net Core如何使用Quartz完成定时调度 一.Quartz使用步骤 创建调度器scheduler,并开启 创建Job作业 ...

  2. Asp.Net Core 使用Quartz基于界面画接口管理做定时任务

    今天抽出一点点时间来造一个小轮子,是关于定时任务这块的. 这篇文章主要从一下几点介绍: 创建数据库管理表 创建web项目 引入quarzt nuget 包 写具体配置操作,实现定时任务处理 第一步:创 ...

  3. 【半译】在ASP.NET Core中创建内部使用作用域服务的Quartz.NET宿主服务

    在我的上一篇文章中,我展示了如何使用ASP.NET Core创建Quartz.NET托管服务并使用它来按计划运行后台任务.不幸的是,由于Quartz.NET API的工作方式,在Quartz作业中使用 ...

  4. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  5. Asp.Net Core 中利用QuartzHostedService 实现 Quartz 注入依赖 (DI)

    QuartzHostedService  是一个用来在Asp.Net Core 中实现 Quartz 的任务注入依赖的nuget 包: 基本示例如下: using System; using Syst ...

  6. 在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

    在这篇文章中,我将介绍如何使用ASP.NET Core托管服务运行Quartz.NET作业.这样的好处是我们可以在应用程序启动和停止时很方便的来控制我们的Job的运行状态.接下来我将演示如何创建一个简 ...

  7. ASP.NET Core托管运行Quartz.NET作业调度详解

    Quartz.NET这么NB的作业调度系统,不会还行?   今天介绍一下Quartz.NET的托管运行,官网传送门. 一.前言 Quartz.NET,按官网上的说法,是一款功能齐全的任务调度系统,从小 ...

  8. 在 ASP.NET Core和Worker Service中使用Quartz.Net

    现在有了一个官方包Quartz.Extensions.Hosting实现使用Quartz.Net运行后台任务,所以把Quartz.Net添加到ASP.NET Core或Worker Service要简 ...

  9. 基于Asp.Net Core 5.0依赖Quartz.Net框架编写的任务调度web管理平台

    源码地址: https://github.com/246850/Calamus.TaskScheduler 演示地址:http://47.101.47.193:1063/ 1.Quartz.NET框架 ...

随机推荐

  1. ubuntu解决安装速度问题

    速度慢得原因:linux系统很多的软件源链接都是在国外服务器上,由于国内防火墙影响导致下载速度极慢,甚至超时. 解决办法一:购买梯子 这样你就可以快速的下载国外服务器的软件包了,但是你得有个可靠得梯子 ...

  2. 聊聊kafka-client的源码

    一,感想 kafka 客户端代码很早以前 我就想研究借鉴一下,我前前后后至少阅读过三遍源码,我发现我看不下去,不知道为啥这么写,在次期间,我也参考了很多的网上的源码分析,我发现自己依然一知半解的, 慢 ...

  3. Java基础教程——打印流

    打印流 打印流可以把原本输出到控制台的信息输出到文件中.PrintStream是字节打印流(还有个对应的字符打印流是PrintWriter,这里不涉及) System类中有个变量: public fi ...

  4. redis cluster可用性测试

    上一节,我们用三台redis组成了cluster,现在我们停掉一台试试: 比较奇怪的是,在停掉其中一台服务器之前建立的链接仍然可以正常执行命令,当我们断开重连时,命令就都被拒绝了: 关联知识: 什么时 ...

  5. java17(面向对象)

    1.面向过程:所有事情都是按顺序一件件做,未知主体 买菜,做饭,吃饭,洗碗 面向对象:将功能封装到对象之中,让对象去实现功能 去饭馆,告诉服务员要吃啥,然后等着端上来. 面向对象的目的: 复杂的东西简 ...

  6. angular中datetime-local属性使用ng-model报错

    项目需求是将年月日格式更改为年月日时分的格式展示,翻遍了整个项目没找到符合的组件,自己现敲一个也来不及,只好直接使用原生自带的组件--datetime-local.之前都是用的vue写项目,第一次接触 ...

  7. 20191017_datatable.select() 数据源中没有dataRow

    filterStr =" 记录时间 >= '2019/10/17 00:00:00' and 记录时间 <='2019/10/20 23:59:59' " 代码: dg ...

  8. 使用cmd制作图片木马

    我们可以使用windows下自带的cmd制作图片木马,配合文件包含漏洞可以达到getshell的目的 我们找到一张图片:kiss.jpg 如图: 写好一句话木马:chopper.php 将两者放在同一 ...

  9. ThreadLocal原理记录,别被坑了!!

    简介 ThreadLocal的用处 ThreadLocal是为了将数据记录一份到某个线程里,确保该数据线程安全 例如数据库的Connection放入ThreadLocal,一个事务会用到很多DAO,但 ...

  10. 【Codeforces 1097F】Alex and a TV Show(bitset & 莫比乌斯反演)

    Description 你需要维护 \(n\) 个可重集,并执行 \(m\) 次操作: 1 x v:\(X\leftarrow \{v\}\): 2 x y z:\(X\leftarrow Y \cu ...