我们经常遇到需要定时执行某些任务的情况,比如清理缓存、异步结果轮询等,如果不打算造轮子,那么选择一款合适的定时任务组件就很关键了。所幸,.Net 世界中的选项并不多:)

选型

主要有以下四款:

  • Quartz.Net:移植自 Java 生态的 Quartz,久经考验、成熟稳重,只是个人感觉有点过度设计,初次接触容易让人困惑;
  • Coravel:提供任务调度,缓存,排队,邮件,事件广播等功能,全面但不专一;
  • Hangfire:最大的特点是内置控制面板。分为社区版和商用版。github 上,这四款组件它的点赞数最多,可见其收欢迎程度。不过它更像一个定时任务管理解决方案,所涵盖的功能除任务本身,还涉及到账户管理、图表管理、告警系统等;
  • FluentScheduler:似乎是这四款中最易上手的,但是已经有段时间没更新了。

稳妥起见,项目前期建议选择坑少、专注、轻量、社区较为活跃的组件 Quartz.Net,再择机使用 Hangfire 打造一个任务管理中心。

如前所述,Quartz.Net 有过度设计的嫌疑,2.x 版本时期配置方式的杂乱可见一斑。虽然现在已经迭代到 3.x,一些概念还是需要花心思理解下。

主要概念

我们围绕下面这个代码片段展开:

var schedulerFactory = builder.Services.GetRequiredService<ISchedulerFactory>();
var scheduler = await schedulerFactory.GetScheduler(); // define the job and tie it to our HelloJob class
var job = JobBuilder.Create<HelloJob>()
.WithIdentity("myJob", "group1")
.UsingJobData("way", "email")
.Build(); // Trigger the job to run now, and then every 40 seconds
var trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(40)
.RepeatForever())
.Build(); await scheduler.ScheduleJob(job, trigger);

Jobs and Triggers

Quartz.Net 将任务业务逻辑(Jobs)和任务执行时间计划(Triggers)做了分离。官方的解释是两者可以独立管理,还可以多个 Trigger 触发同一个 Job;有没有必要见仁见智

Job 和 Trigger 还有group的概念,比如多个模块都有各自清理缓存的任务,可以将这些任务划分为同一个组。但是在实操过程中,group 除了语义上的指示,似乎并没有直接的其它作用;也许它可以用于业务端的扩展,不过私以为组件不应将可能的业务需求作为自己的设计点,且业务扩展不应依赖某个具体组件

JobDetail

上述代码中变量 job 的类型并非IJob,而是IJobDetail,也就是说,Quartz.Net 调度维持的是 IJobDetail 实例,而 Job 实例,是在每次任务执行的时间点实例化的,执行完就销毁,实例状态并不能延续,需要借助 IJobDetail 实例存取每次执行后更新的状态。

JobDataMap

Job 实例状态保存在 JobDataMap 中,构造 JobDetail 对象时使用 .UsingJobData(key, value) 定义键值对。Trigger 也有 JobDataMap,是为上面提到的———多个 Trigger 触发同一个 Job——这种情况设计的。

使用方式如下:

public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key; // 可使用 context.MergedJobDataMap 同时获取 Trigger 提供的键值
JobDataMap dataMap = context.JobDetail.JobDataMap; string way = dataMap.GetString("way"); await Console.Error.WriteLineAsync("Instance " + key + " of HelloJob is send by: " + way);
}
}

另外要注意的是,用来修饰 Job 类的特性DisallowConcurrentExecution,其实约束的是 JobDetail,即对于 JobDetail-A,同一时间,只能有一个 Job 使用它。也就是说,可以同时激活多个 HelloJob 对象,只要它们关联的 JobDetail 不同即可(当然了,实际我们也只能操作多个不同的 JobDetail 去激活对应的 Job)。

关于 JobDetail 的解释,官方文档写得过于复杂,其实它的目的就是为了解决 Job 实例并非单例的问题,那么作者为什么不干脆将 Job 实例单例化呢?根本没必要创造一个多余的 JobDetail 的概念,令人费解。如果是因为并发考量,那么应该从 Job 定义入手,加入多线程支持。

如果项目中使用了 IOC,那么我们可以选择不使用 JobDataMap,而是将实例状态保存在自定义对象中,在每次实例化 Job 对象时注入。

Misfire 处理方案

当一个 Job 在规定时间点没有被触发执行(比如线程池里面没有可用的线程、Job 被暂停等),且超时时间超过 misfireThreshold 配置的值(默认为60秒),则作业会被调度程序认为Misfire

当系统恢复后(有空闲线程、Job 被恢复等),调度程序会根据配置的 Misfire 策略处理已错过的那些触发点。

参考资料

Quartz misfire详解

Quartz.Net 主要概念介绍和吐槽的更多相关文章

  1. 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之集群概念介绍(一)

    集群概念介绍(一)) 白宁超 2015年7月16日 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习 ...

  2. Linux LVM硬盘管理之一:概念介绍

    一.LVM概念介绍: LVM是 Logical Volume Manager(逻辑卷管理)的简写,它由Heinz Mauelshagen在Linux 2.4内核上实现.LVM将一个或多个硬盘的分区在逻 ...

  3. Java SE/ME/EE的概念介绍

    转自 Java SE/ME/EE的概念介绍 多数编程语言都有预选编译好的类库以支持各种特定的功能,在Java中,类库以包(package)的形式提供,不同版本的Java提供不同的包,以面向特定的应用. ...

  4. rocketMq概念介绍

    rocketMq官网 http://rocketmq.apache.org/ rocketMq逻辑概念介绍 rocketMq逻辑图 备注:    改图片分享自李占卫的网上家园 说明: 在rocketM ...

  5. java 并发多线程 锁的分类概念介绍 多线程下篇(二)

    接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...

  6. Airflow Python工作流引擎的重要概念介绍

    Airflow Python工作流引擎的重要概念介绍 - watermelonbig的专栏 - CSDN博客https://blog.csdn.net/watermelonbig/article/de ...

  7. spring batch (一) 常见的基本的概念介绍

    SpringBatch的基本概念介绍 内容来自<Spring Batch 批处理框架>,作者:刘相. 一.配置文件 在项目中使用spring batch 需要在配置文件中声明: 事务管理器 ...

  8. helm-chart-1-简单概念介绍-仓库搭建-在rancher上的使用

    简单的概念介绍: Chart是helm管理的应用的打包格式,一个chart对应一个或一套应用.内部是一系列的yaml描述文件,以为为yaml 服务的文件. 三个部分,helm .tiller.repo ...

  9. Netty重要概念介绍

    Netty重要概念介绍 Bootstrap Netty应用程序通过设置bootstrap(引导)类开始,该类提供了一个用于网络成配置的容器. 一种是用于客户端的Bootstrap 一种是用于服务端的S ...

  10. 《AngularJS深度剖析与最佳实践》笔记: 第二章 概念介绍

    第二章 概念介绍 2.1 什么是UI? 用户界面包括内容(静态信息+动态信息), 外观, 交互. 在前端技术栈中分别由HTML, CSS和JS负责. 进一步抽象, 分别对应于MVC三个主要部分: Mo ...

随机推荐

  1. Springboot自动装配源码及启动原理理解

    Springboot自动装配源码及启动原理理解 springboot版本:2.2.2 传统的Spring框架实现一个Web服务,需要导入各种依赖JAR包,然后编写对应的XML配置文件 等,相较而言,S ...

  2. 疫情可视化part3

    前言 之前在part2中说的添加自定义主题配色已经开发完成了,除此之外我还添加了一些的3d特效. 前期文章 这是part1的文章:https://blog.csdn.net/xi1213/articl ...

  3. O-MVLL:支持ARM64的基于LLVM的代码混淆模块

    O-MVLL介绍 O-MVLL的开发灵感来自于另一个著名的基于LLVM的代码混淆项目ollvm,并在其基础上做了创新和改进.O-MVLL的混淆逻辑实现方式也是通过LLVM Pass,支持也仅会支持AR ...

  4. 互斥锁 线程理论 GIL全局解释器锁 死锁现象 信号量 event事件 进程池与线程池 协程实现并发

    目录 互斥锁 multiprocessing Lock类 锁的种类 线程理论 进程和线程对比 开线程的两种方式(类似进程) 方式1 使用Thread()创建线程对象 方式2 重写Thread类run方 ...

  5. DOM(原生js事件绑定)

    一:原生js事件绑定 1.开关灯案例 <!DOCTYPE html> <html lang="en"> <head> <meta char ...

  6. 可视化软件Navicat,python操作MySQL

    可视化软件Navicat 第三方开发的用来充当数据库客户端的简单快捷的操作界面 无论第三方软件有多么的花里胡哨,底层的本质还是SQL 能够操作数据库的第三方可视化软件有很多,其中针对MySQL最出名的 ...

  7. 软件开发架构、构架趋势、OSI七层协议

    目录 软件开发架构 构架总结 网络编程前戏 OSI七层协议简介 OSI七层协议值之物理连接层 OSI七层协议之数据链层 网络相关专业名词 OSI七层协议之网络层 IP协议: IP地址特征: IP地址分 ...

  8. python之元组(tuple)知识点

    元组与列表都是容器,两个的区别在于: 1.元组使用的是小括号,列表使用的是方括号 2.元组一旦定义不可修改,而列表是可以随意变更 创建元组 元组的创建与列表大同小异,逗号在元组中充当了元组的灵魂,创建 ...

  9. Linux中的infuxdb安装及数据迁移

    一.安装influxdb 1.更新yum源 cat <<EOF | sudo tee /etc/yum.repos.d/influxdb.repo   [influxdb]   baseu ...

  10. 刷题笔记——1267.A+B Problem

    题目 1267.A+B Problem 代码 while True: try: a,b=map(int,input().strip().split()) print(a+b) except: brea ...