前言

本来打算昨天都开始写这篇,就因为要把小团队的博客整理汇总,一看二哈的博客那么多,一个个复制粘贴肯定麻烦(其实是我自己觉得复制麻烦),所以穿插着写了个小爬虫,后续写差不多了就拿出来晾晾吧(py菜鸡水平)。

之前开发的时候,忽略了记录,等到想写点儿啥跟后台有关的东西的时候,还得一点点回忆,最近是因为同事给我说,"哎,每个月把数据给我统计下做个界面展示啊"。一想到每个月我要做次操作就头疼,咦,不对,这不就是写个定时任务就搞定了嘛。

Quartz

其实在选这个定时器的类库的时候,我在Hangfire两者间徘徊,后来是想到不管用什么方法什么工具都是次要的,主要看你怎么用,用到哪,图形界面是需要但不是必要,分秒级别的控制也都是看你自己业务需要,定时器就后台挂起运行就行了没必要让我看见,想操作了再说吧,就这样愉快的决定使用Quartz

首先,依然是在我们Util的工程引入包。

引入完成后,在我们的入口Startup中添加实例的注册声明。

        public IServiceProvider ConfigureServices(IServiceCollection services)
{
ServiceInjection.ConfigureRepository(services); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
//任务调度
services.TryAddSingleton<ISchedulerFactory, StdSchedulerFactory>();
}

SchedulerFactory任务调度就好比一个公司的老大,Trigger就是一个项管,Job就是苦逼的码农,老大想要一天搞个app,就跟项管说一句,我要一天后要东西,这时候项管心里就有数了,一天后的那个时间,找到码农,直接剥夺他的代码执行,好了app出来了,苦逼的结束并不意味着真的结束,这老大一看可以啊,好了,以后每天我要一个成品app,如此循环往复,项管不厌其烦,码农换了又换(当然job不会)。

项管还会有多个,每个项管下面可不止一个码农。

像这样的情况可能有些夸张,但是类似的情况却真实存在。

ok,完了之后,我们来创建一个MyJob

    public class MyJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
return Task.Run(() =>
{
LogUtil.Debug("执行MyJob");
});
}
}

之后我们来写个简单的QuartzUtil

    public class QuartzUtil
{
private static ISchedulerFactory _schedulerFactory;
private static IScheduler _scheduler; /// <summary>
/// 添加任务
/// </summary>
/// <param name="type">类</param>
/// <param name="jobKey">键</param>
/// <param name="trigger">触发器</param>
public static async Task Add(Type type, JobKey jobKey, ITrigger trigger = null)
{
Init();
_scheduler = await _schedulerFactory.GetScheduler(); await _scheduler.Start(); if (trigger == null)
{
trigger = TriggerBuilder.Create()
.WithIdentity("april.trigger")
.WithDescription("default")
.WithSimpleSchedule(x=>x.WithMisfireHandlingInstructionFireNow().WithRepeatCount(-1))
.Build();
}
var job = JobBuilder.Create(type)
.WithIdentity(jobKey)
.Build(); await _scheduler.ScheduleJob(job, trigger);
}
/// <summary>
/// 恢复任务
/// </summary>
/// <param name="jobKey">键</param>
public static async Task Resume(JobKey jobKey)
{
Init();
_scheduler = await _schedulerFactory.GetScheduler();
LogUtil.Debug($"恢复任务{jobKey.Group},{jobKey.Name}");
await _scheduler.ResumeJob(jobKey);
}
/// <summary>
/// 停止任务
/// </summary>
/// <param name="jobKey">键</param>
public static async Task Stop(JobKey jobKey)
{
Init();
_scheduler = await _schedulerFactory.GetScheduler();
LogUtil.Debug($"暂停任务{jobKey.Group},{jobKey.Name}");
await _scheduler.PauseJob(jobKey);
}
/// <summary>
/// 初始化
/// </summary>
private static void Init()
{
if (_schedulerFactory == null)
{
_schedulerFactory = AprilConfig.ServiceProvider.GetService<ISchedulerFactory>();
}
}
}

感谢jiulang指点,异步编程避免使用async void,因为这样会导致异常无法捕获。

触发器的使用,有很多种方式,可以使用简单的执行一次/多久执行一次/循环执行几次等等。

还有可以使用Cron表达式:

简单来说,corn从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份,举个例子,就像开头说的,让我每隔一个月执行一次统计,写法就是 0 0 0 1 * ?,当然这就有涉及到什么符号的问题了,这种不需要强记,需要的时候查下就行,推荐一个工具站吧,Cron校验工具

测试

感觉我的博客内容好单调,内容框架就是开头,代码,测试,结尾,唉

不过做啥东西,测试少不了,最起码你的东西能用,才说明可行。

我们在Values添加一个方法,这里我们5s一执行(懒得等)。

        [HttpGet]
[Route("QuartzTest")]
public void QuartzTest(int type)
{
JobKey jobKey = new JobKey("demo","group1");
switch (type)
{
//添加任务
case 1:
var trigger = TriggerBuilder.Create()
.WithDescription("触发器描述")
.WithIdentity("test")
//.WithSchedule(CronScheduleBuilder.CronSchedule("0 0/30 * * * ? *").WithMisfireHandlingInstructionDoNothing())
.WithSimpleSchedule(x=>x.WithIntervalInSeconds(5).RepeatForever().WithMisfireHandlingInstructionIgnoreMisfires())
.Build();
QuartzUtil.Add(typeof(MyJob), jobKey, trigger);
break;
//暂停任务
case 2:
QuartzUtil.Stop(jobKey);
break;
//恢复任务
case 3:
QuartzUtil.Resume(jobKey);
break;
}
}

让我们来愉快的运行吧,记得appsettings配置个路径访问白名单。

一番1,2,3输入完之后,我们来看下日志。

  • 执行任务--- ok
  • 暂停任务--- ok
  • 恢复任务--- ok

问题及解决方法

但是问题出现了,暂停恢复后,连执行了多次(具体看你间隔时间以及你的频率),这个是有点儿怪异,当时我记得这个问题让我鼓捣了好半天,也是各种查资料查方法,但实际呢这个是Quartz的保护机制,为了防止你的操作是因为不可预知的问题导致的,所以有个重做错过的任务,另外我们的代码中触发器也有这个配置WithMisfireHandlingInstructionIgnoreMisfires

我们来去掉这个重做机制并测试。

CronTrigger

规则 介绍
withMisfireHandlingInstructionDoNothing 不触发立即执行; 等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
withMisfireHandlingInstructionIgnoreMisfires 以错过的第一 个频率时间立刻开始执行; 重做错过的所有频率周期后; 当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行
withMisfireHandlingInstructionFireAndProceed 以当前时间为触发频率立刻触发一次执行; 然后按照Cron频率依次执行

SimpleTrigger

规则 介绍
withMisfireHandlingInstructionFireNow 以当前时间为触发频率立即触发执行; 执行至FinalTIme的剩余周期次数;以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到; 调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionIgnoreMisfires 以错过的第一个频率时间立刻开始执行; 重做错过的所有频率周期;当下一次触发频率发生时间大于当前时间以后,按照Interval的依次执行剩下的频率; 共执行RepeatCount+1次
withMisfireHandlingInstructionNextWithExistingCount 不触发立即执行; 等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数; 以startTime为基准计算周期频率,并得到FinalTime; 即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNowWithExistingCount 以当前时间为触发频率立即触发执行; 执行至FinalTIme的剩余周期次数; 以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到; 调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionNextWithRemainingCount 不触发立即执行; 等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数; 以startTime为基准计算周期频率,并得到FinalTime; 即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNowWithRemainingCount 以当前时间为触发频率立即触发执行; 执行至FinalTIme的剩余周期次数; 以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到; 调整后的FinalTime会略大于根据starttime计算的到的FinalTime值

配置规则介绍参考:https://blog.csdn.net/yangshangwei/article/details/78539433

之前在net framework遇到过一个问题,IIS回收问题,网站在20分钟无请求后就停了,任务也紧跟着停了,当时的解决方法是做个windows服务来定时请求网站保持活跃,当然也可以通过禁止回收来保持网站一直运行。

net core中还没部署运行,如果有相关问题,后续也会补充上来一起交流解决。

小结

定时任务在一个后台系统中一般使用场景还算广泛,主要是sql数据统计,sql/文件备份,定时推送等,具体问题具体分析,net core 3.0都已经问世了,学无止境啊。

net core WebApi——定时任务Quartz的更多相关文章

  1. .Net Core 3.1浏览器后端服务(五) 引入定时任务Quartz.Net

    一.前言 近期项目中遇到一些需求,需要定时写入数据库,定时刷新缓存的问题,因此需要引入任务调度机制. 我的选择是使用 Quartz.Net,使用的版本是 3.2.4 这里强调一点:3.x的版本与2.x ...

  2. spring学习总结(mybatis,事务,测试JUnit4,日志log4j&slf4j,定时任务quartz&spring-task,jetty,Restful-jersey等)

    在实战中学习,模仿博客园的部分功能.包括用户的注册,登陆:发表新随笔,阅读随笔:发表评论,以及定时任务等.Entity层设计3张表,分别为user表(用户),essay表(随笔)以及comment表( ...

  3. .Net Core WebAPI 基于Task的同步&异步编程快速入门

    .Net Core WebAPI 基于Task的同步&异步编程快速入门 Task.Result async & await 总结 并行任务(Task)以及基于Task的异步编程(asy ...

  4. asp.net core webapi之跨域(Cors)访问

    这里说的跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作 ...

  5. ASP.NET Core WebAPI 开发-新建WebAPI项目

    ASP.NET Core WebAPI 开发-新建WebAPI项目, ASP.NET Core 1.0 RC2 即将发布,我们现在来学习一下 ASP.NET Core WebAPI开发. 网上已经有泄 ...

  6. Asp.net Core WebApi 使用Swagger做帮助文档,并且自定义Swagger的UI

    WebApi写好之后,在线帮助文档以及能够在线调试的工具是专业化的表现,而Swagger毫无疑问是做Docs的最佳工具,自动生成每个Controller的接口说明,自动将参数解析成json,并且能够在 ...

  7. AngularJS 2调用.net core WebAPI的几个坑

    前几天,按照AngularJS2的英雄指南教程走了一遍,教程网址是http://origin.angular.live/docs/ts/latest/tutorial/. 在步骤完成后,又更进一步,在 ...

  8. Asp.Net Core WebApi学习笔记(四)-- Middleware

    Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...

  9. Net Core WebAPI

    Net Core WebAPI .Net Core WebAPI 基于Task的同步&异步编程快速入门 Task.Result async & await 总结 并行任务(Task)以 ...

随机推荐

  1. 【实践总结】给Centos和Ubuntu设置静态网络IP以及配置ssh功能

    作为一名以Windows平台为主的开发者,在接触和使用Linux系统的过程中总会遇到一系列的问题.每当这时候,我相信大部分人是和我一样的处理办法,就是网上各种搜索尝试直到问题解决为止,而有些问题,前后 ...

  2. (四十七)c#Winform自定义控件-树表格(treeGrid)

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  3. .NET Core 单元测试

    应用程序测试的类型很多,包括集成测试,Web 测试,负载测试等.在最底层的是单元测试,此测试可以测试单个软件组件或方法.单元测试一般只测试开发人员的代码,不应该测试基础结构普.问题,如数据库,文件系统 ...

  4. (五十)c#Winform自定义控件-滑块

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  5. MySQL数据库安装步骤

    目录 MySQL数据库安装 MySQL数据库安装 MySQL Windows下载地址:https://dev.mysql.com/downloads 我们这里选择5.6.45版本下载,下载zip. 点 ...

  6. Java IO体系之File类浅析

    Java IO体系之File类浅析 一.File类介绍 位于java.io下的Java File类以抽象的方式代表文件名和目录路径名.该类主要用于文件和目录的创建.文件的查找和文件的删除等.File对 ...

  7. centos6在安装wdcp以后,导入MySQLdb报错问题

    为了方便linux的使用,会先安装好wdcp对服务器进行管理.在装好wdcp会对一些nginx,mysql等自动安装,但是mysql的安装目录会在/www/wdlinux这个目录下,跟一般的mysql ...

  8. Delphi - 通过WinAPI GetCursorPos实现鼠标位置的实时显示

    通过WinAPI GetCursorPos实现鼠标位置的实时显示 有时候我们需要将鼠标的位置实时抓取出来,可以通过如下方式实现. 添加一个Timer控件,执行间隔改为100ms,双击控件输入如下代码: ...

  9. HTML 画布(摘自菜鸟教程)

    颜色.样式和阴影 属性 描述 fillStyle 设置或返回用于填充绘画的颜色.渐变或模式. strokeStyle 设置或返回用于笔触的颜色.渐变或模式. shadowColor 设置或返回用于阴影 ...

  10. d3.js 教程 模仿echarts柱状图

    由于最近工作不是很忙,隧由把之前的charts项目用d3.js重写的一下,其实d3.js文档很多,但是入门不是很难,可是想真的能做一个完成的,交互良好的图还是要下一番功夫的.今天在echarts找到了 ...