最佳实践

JobDataMap
  1. 建议只存储基本数据(含String),避免序列化问题

  2. 作业执行期间,JobDetail和Trgger的底层共用一个JobDataMap 实例,因此Trigger的数据会覆盖Job中相同key的值。

  3. 每个独立触发器的JobDataMap 是独立的

  4. 在作业执行期间,建议使用MergedJobDataMap去检索key

    // 不建议
    var badMethod = context.JobDetail.JobDataMap.GetString("a-value");
    var alsoBadMethod = context.Trigger.JobDataMap.GetString("a-value");
    // 建议
    var goodMethod = context.MergedJobDataMap.GetString("a-value");
Job

​ 为统一作业的名称和群组,建议在IJob实现时就声明一个静态JobKey,便于触发器赋值使用

public class SomeJob : IJob
{
// 定义当前作业的名称和群组
public static readonly JobKey Key = new JobKey("job-name", "group-name"); public Task Execute(IJobExecutionContext context) { /* elided */ }
}
// 创建触发器
public async Task DoSomething(IScheduler schedule, CancellationToken ct)
{
var trigger = TriggerBuilder.Create()
.WithIdentity("a-trigger", "a-group")
.ForJob(SomeJob.Key)
.StartNow()
.Build(); await schedule.ScheduleJob(trigger, ct)
}
// 设置当前作业的触发器时
public async Task DoSomething(IScheduler schedule, CancellationToken ct)
{
await schedule.TriggerJob(SomeJob.Key, ct)
}
Trigger

​ 使用TriggerUtils进行触发器时间上的操作

  1. 根据触发器获得执行时间列表

    static IReadOnlyList ComputeFireTimes(IOperableTrigger trigg, ICalendar? cal, int numTimes)

    // 设置时间
    DateTimeOffset startCalendar = DateBuilder.DateOf(9, 30, 17, 1, 1, 2023);
    // 设置日期的触发器,间隔90天执行一次,从2023-01-01 17:30:09开始
    var dailyTrigger = new CalendarIntervalTriggerImpl
    {
    StartTimeUtc = startCalendar,
    RepeatIntervalUnit = IntervalUnit.Day,
    RepeatInterval = 90 // every ninety days
    };
    // 2023年1月1日加上360天 4*90 四个90天的循环
    DateTimeOffset targetCalendar = startCalendar.AddDays(360);
    // 获得从0开始的6次循环(0-5)
    var fireTimes = TriggerUtils.ComputeFireTimes(dailyTrigger, null, 6);
    // 获得第五次的循环日期,2023-01-01 17:30:09当天也会执行一次
    DateTimeOffset fifthTime = fireTimes[4]; // get the fifth fire time
    // 两次获得的日期相同
    Assert.AreEqual(targetCalendar, fifthTime, "Day increment result not as expected.");
  2. 获得触发器最后一次执行的时间

    static DateTimeOffset? ComputeEndTimeToAllowParticularNumberOfFirings(IOperableTrigger trigger, ICalendar? calendar, int numberOfTimes)

    // 设置时间
    DateTimeOffset startCalendar = DateBuilder.DateOf(9, 30, 17, 1, 1, 2023);
    // 设置日期的触发器,间隔90天执行一次,从2023-01-01 17:30:09开始
    var dailyTrigger = new CalendarIntervalTriggerImpl
    {
    StartTimeUtc = startCalendar,
    RepeatIntervalUnit = IntervalUnit.Day,
    RepeatInterval = 90 // every ninety days
    };
    // 2023年1月1日加上360天 4*90 四个90天的循环
    DateTimeOffset targetCalendar = startCalendar.AddDays(360);
    // 获得从0开始的4次循环(0-3),然后返回第四次执行的时间
    var endFireTimes = TriggerUtils.ComputeFireTimes(dailyTrigger, null, 4);
    // 两次获得的日期相同
    Assert.AreEqual(targetCalendar, fifthTime, "Day increment result not as expected.");
  3. 在两个日期之间触发器执行的时间列表

    static IReadOnlyList ComputeFireTimesBetween(IOperableTrigger trigg, ICalendar? cal, DateTimeOffset from, DateTimeOffset to)

    // 设置触发器
    var trigger = (IOperableTrigger) TriggerBuilder.Create()
    .WithDailyTimeIntervalSchedule(x => x
    .InTimeZone(TZConvert.GetTimeZoneInfo("GTB Standard Time"))
    // 每天从0点开始
    .StartingDailyAt(new TimeOfDay(0, 0, 0))
    // 跳过21点,第二次循环跳过了20和21点
    .EndingDailyAt(new TimeOfDay(22, 0, 0))
    // 15分钟循环一次
    .WithInterval(15, IntervalUnit.Minute)
    // 触发失效什么也不做
    .WithMisfireHandlingInstructionDoNothing()
    )
    .Build();
    var from = new DateTimeOffset(2023, 1, 31, 0, 0, 0, TimeSpan.Zero);
    var to = new DateTimeOffset(2023, 2, 2, 0, 0, 0, TimeSpan.Zero);
    //理论上会执行 ((23个小时+22个小时)*60分钟)/(15分钟/次) - 1次 = 179次
    var times = TriggerUtils.ComputeFireTimesBetween(trigger, null, from, to);
    // 小于200次
    Assert.That(times.Count, Is.LessThan(200));
ADO.NET JobStore
  1. 使用Quartz.NET的API写入数据库,不要直接操作数据库表
  2. 死锁
    • 触发器中的作业未被正确执行
  3. 集群之间的问题
    • 非集群与集群不能共用一个数据库
  4. 数据源连接大小
    • 建议将数据源最大连接大小配置为至少为线程池中的工作线程数加上三
    • 如果使用Api则需要额外的连接
Daylight Savings Time

​ 夏令时-中国在执行了6年就取消了,因此该最佳实现目前不适用于无夏令时的地区。

​ 因为SimpleTriggers为精确到毫秒,因此不受夏令时的影响;CronTriggers在转换的时候会受到夏令时的影响,可能会多触发一次;CalenderIntervalTrigger也会受影响,会导致偏移一小时的误差。

Jobs
  • 长时间运行的作业会阻止其他线程运行(例如已运行作业数量与线程池数量相等)
  • 不建议在作业中使用Thread.Sleep(),因为他会让线程一直被占用,建议在使用Sleep的位置重新安排自身或者其他作业来替代等待(通常等待是因为条件不为true,因此可以将该判断作为作业执行,周期短)
  • 因为作业是周期性的,因此当作业异常时会一直触发异常,此时异常过多会导致应用程序不稳定。建议在catch处解决异常或重新开启新的作业来消解异常的发生。
Listener
  • 不建议监听中执行大量工作,因为作业更合适做这些工作,监听只是监听。
  • 同Jobs的异常处理,异常多了容易崩溃。
对外调用
  • 不建议对外暴露,对于本机过于危险(例如NativeJob和SendEmailJob都可以用于恶意的目的)

Quartz.Net 官方教程 Best Practices的更多相关文章

  1. Unity性能优化(3)-官方教程Optimizing garbage collection in Unity games翻译

    本文是Unity官方教程,性能优化系列的第三篇<Optimizing garbage collection in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...

  2. Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译

    本文是Unity官方教程,性能优化系列的第四篇<Optimizing graphics rendering in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...

  3. Quartz.net官方开发指南系列篇

    Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...

  4. Unity性能优化(2)-官方教程Diagnosing performance problems using the Profiler window翻译

    本文是Unity官方教程,性能优化系列的第二篇<Diagnosing performance problems using the Profiler window>的简单翻译. 相关文章: ...

  5. Unity性能优化(1)-官方教程The Profiler window翻译

    本文是Unity官方教程,性能优化系列的第一篇<The Profiler window>的简单翻译. 相关文章: Unity性能优化(1)-官方教程The Profiler window翻 ...

  6. jeecg表单页面控件权限设置(请先看官方教程,如果能看懂就不用看这里了)

    只是把看了官方教程后,觉得不清楚地方补充说明一下: 1. 2. 3. 4.用"jeecgDemoController.do?addorupdate"这个路径测试,不出意外现在应该可 ...

  7. [转]Google Guava官方教程(中文版)

    Google Guava官方教程(中文版) http://ifeve.com/google-guava/

  8. Google Guava官方教程(中文版)

    Google Guava官方教程(中文版) 原文链接  译文链接 译者: 沈义扬,罗立树,何一昕,武祖  校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...

  9. OpenGL官方教程——着色器语言概述

    OpenGL官方教程——着色器语言概述 OpenGL官方教程——着色器语言概述 可编程图形硬件管线(流水线) 可编程顶点处理器 可编程几何处理器 可编程片元处理器 语言 可编程图形硬件管线(流水线) ...

  10. [苏飞开发助手V1.0测试版]官方教程与升级报告

           [苏飞开发助手V1.0测试版]官方教程与升级报告导读部分----------------------------------------------------------------- ...

随机推荐

  1. 类的编写模板之简单Java类

    简单Java类是初学java时的一个重要的类模型,一般由属性和getter.setter方法组成,该类不涉及复杂的逻辑运算,仅仅是作为数据的储存,同时该类一般都有明确的实物类型.如:定义一个雇员的类, ...

  2. 驱动开发:内核封装WSK网络通信接口

    本章LyShark将带大家学习如何在内核中使用标准的Socket套接字通信接口,我们都知道Windows应用层下可直接调用WinSocket来实现网络通信,但在内核模式下应用层API接口无法使用,内核 ...

  3. 说一下 ArrayList 和 LinkedList 的区别?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在上一篇文章里,我们聊到了基于动态数组 ArrayList 线性表,今天我们来讨论 ...

  4. PP视频(PPTV聚力)web接口分析

    前言 前几天我想看一个番剧, 正好搜索到了 PP视频,我才知道PP视频就是PPTV聚力,我想把番剧下载下来,结果发现视频竟然不是m3u8格式,而是多段mp4,所以简单的写了个脚本,可以在不登录的情况下 ...

  5. .net6&7中如何优雅且高性能的使用Json序列化

    .net中的SourceGenerator让开发者编可以写分析器,在项目代码编译时,分析器分析项目既有的静态代码,允许添加源代码到GeneratorExecutionContext中,一同与既有的代码 ...

  6. 兼容IE全版本及所有市面浏览器的网页变黑白处理方式

    大家应该有发现最近几天不少网站变成了黑白色,在哀悼日时,很多网站都需要全站变成黑白配色,今天对这个实现的技术做了一些探索性了解,在此进行一个记录分享. 使用的样式部分:下面的css部分想必大家应该都可 ...

  7. 记录一次 MyBatis 批量插入的优化-BatchInsert

    记录在一次项目问题排查过程中,遇到在数据量大的情况下,向数据库批量插入非常耗时长的问题. 1.分析 首先,代码是在 service 中,采用的是 for 循环调用 insert 语句的方式: for( ...

  8. [数学建模]主成分分析法PCA

    最常用的线性降维方法,通过某种线性投影,将高维的数据映射到低维的空间中,并期望在所投影的维度上数据的信息量最大(方差最大),以此使用较少的数据维度,同时保留住较多的原数据点的特性. Q1:为何选取方差 ...

  9. uniapp 打包app 引入高德地图

    一.高德地图注册key值 二.项目中添加配置 三.项目中引用 <view class="home-btom-box" > <view class="ho ...

  10. AcWing341. 洛谷P1073, NOIP2009 最优贸易

    AcWing题目传送门 洛谷题目传送门 题目大意 \(~~~~~~\)一个投机倒把的奸商想要通过城市不太健全的贸易系统坑点钱,任意城市都可以买入或者卖出水晶球,他想尽量在便宜的城市买入,在贵的城市卖出 ...