×

Quartz.NET:.NET 定时任务的天花板,我愿称之为调度之王

独孤求败 独孤求败 发表于2026-05-19 16:50:24 浏览39 评论0

抢沙发发表评论

很多 .NET 开发者一提到"定时任务",脑子里冒出来的方案是啥?

TimerTask.Delay?写个 Windows Service?还是直接用系统级的"任务计划程序"?

如果你还在用这些方案,你的系统迟早会踩到一个坑:任务跑丢了不知道、服务器重启后任务没恢复、分布式环境下任务被重复执行——每一个都足以让运维同学半夜两点爬起来。

Quartz.NET,才是 .NET 世界里定时任务的正确答案。


https://github.com/quartznet/quartznet

一、它是什么?

Quartz.NET 是 Java 世界里鼎鼎大名的 Quartz 调度器的 .NET 移植版,由社区长期维护(主要作者 Marko Lahma),目前支持:

  • .NET Core / .NET 6+

    (netstandard 2.0)
  • .NET Framework 4.6.2+

换句话说,从老旧的 .NET Framework 项目到最新的 .NET 8 项目,都能用。

它的定位是企业级作业调度框架——不是玩具,是真正能扛住生产环境的那种。

图片

二、核心概念:三板斧

Quartz.NET 的设计哲学很简单,理解三个概念,就掌握了 80%:

1. Job——你要执行的工作

创建一个 Job,只需要实现 IJob 接口:

publicclassHelloJob : IJob
{
public ValueTask Execute(IJobExecutionContext context)
    {
        Console.WriteLine($"Hello World! - {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
returndefault;
    }
}

就这么多。没有乱七八糟的继承,不用重写一堆方法。干净利落。

2. Trigger——什么时候执行

Quartz.NET 内置了四种触发器,最常用的是两种:

SimpleTrigger——"每 N 秒执行一次":

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("myTrigger""myGroup")
    .StartNow()
    .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever())
    .Build();

CronTrigger——用 Cron 表达式,精准控制每一个时间点:

ICronTrigger trigger = (ICronTrigger) TriggerBuilder.Create()
    .WithIdentity("myTrigger""myGroup")
    .WithCronSchedule("0 0 9 * * ?")  // 每天早上9点
    .Build();

Cron 表达式的强大之处在于:* /5 * * * ? 是"每 5 分钟",0 0 10 1,15 * ? 是"每月 1 号和 15 号上午 10 点",0,30 * * ? * MON-FRI 是"工作日的每半小时"——没有你表达不了的时间规则

3. Scheduler——调度器,把 Job 和 Trigger 绑在一起

IScheduler scheduler = await StdSchedulerFactory.GetScheduler();
await scheduler.Start();

IJobDetail job = JobBuilder.Create<HelloJob>()
    .WithIdentity("myJob""myGroup")
    .Build();

await scheduler.ScheduleJob(job, trigger);

三行代码,一个定时任务就跑起来了。

图片

三、为什么是 Quartz.NET?

如果你只是需要一个"每分钟跑一次"的简单任务,市面上确实有很多替代方案。但 Quartz.NET 的真正价值,在复杂场景下才完全体现。

1. 分布式集群——任务不重复、不丢失

Quartz.NET 内置集群模式(基于数据库的悲观锁)。多台服务器同时运行同一个调度器,任务只会被一台服务器抢到执行权,不会重复跑,也不会漏跑。

这对微服务架构尤其重要——你不需要再写分布式锁,框架帮你兜底。

2. 持久化——重启不怕,任务还在

默认情况下,Job 信息存在内存(RAMJobStore),速度快但服务器重启会丢失。

切换到ADO-JobStore,任务调度信息全部持久化到数据库。服务器崩了?重启后继续跑,毫厘不差。

3. 丰富的监听机制——每一步都能插一脚

// Job 执行前后的钩子
scheduler.ListenerManager.AddJobListener(myJobListener);

// Trigger 触发时的钩子
scheduler.ListenerManager.AddTriggerListener(myTriggerListener);

// Scheduler 本身的钩子(启动、关闭、报错……)
scheduler.ListenerManager.AddSchedulerListener(mySchedulerListener);

你可以用它做任务监控、告警、审计日志,每一个 Job 的成功与失败都不再是黑箱。

4. 日历排除——节假日不跑任务

很多业务场景需要"工作日才跑"或者"节假日不跑"。Quartz.NET 内置日历机制,可以把任意日期段排除在调度之外:

// 排除整个2026年春节(2月10日-2月17日)
var holidayCalendar = new AnnualCalendar();
holidayCalendar.SetDayExcluded(new DateTime(2026210), true);
// ……省略更多日期
scheduler.AddCalendar("holidays", holidayCalendar, falsefalse);

trigger = TriggerBuilder.Create()
    .ModifiedByCalendar("holidays")  // 绑定日历
    .WithCronSchedule("0 0 9 * * ?")
    .Build();

再也不用在 Cron 表达式里手写一堆排除逻辑。

5. OpenTelemetry 支持——可观测性原生集成

v4.0 引入了 Quartz.OpenTelemetry.Instrumentation,Job 的执行链路、耗时、异常全部可以接入你的可观测性系统。

四、快速上手:三分钟跑起来

第一步:安装

dotnet add package Quartz

第二步:写一个 Job

publicclassSendEmailJob : IJob
{
    publicasync ValueTask Execute(IJobExecutionContext context)
        {
        var subject = context.JobDetail.JobDataMap.GetString("Subject");
        Console.WriteLine($"发送邮件:{subject}");
await Task.CompletedTask;
    }
}

第三步:配置调度

var scheduler = await StdSchedulerFactory.GetScheduler();
await scheduler.Start();

var job = JobBuilder.Create<SendEmailJob>()
    .WithIdentity("sendEmail""notification")
    .UsingJobData("Subject""每日报告")
    .Build();

var trigger = TriggerBuilder.Create()
    .WithIdentity("dailyTrigger""notification")
    .WithCronSchedule("0 30 8 * * ?")  // 每天早上8:30
    .Build();

await scheduler.ScheduleJob(job, trigger);

完事。

图片

五、注意事项

1. 并发控制要主动声明

默认情况下,同一个 Job 可以被并发触发。如果你的任务不支持并发(比如操作共享文件),要用特性声明:

[DisallowConcurrentExecution]
publicclassFileProcessingJob : IJob { }

2. JobDetail 的数据传递

JobDataMap 是在 Job 之间传递参数的标准方式,但要注意序列化问题——复杂对象建议用 JSON 序列化后再存取。

3. v4.0 正在开发中

目前 master 分支是 v4.0 版本,有较多 Breaking Changes。如果追求稳定,建议使用 NuGet 上的稳定版 3.x 系列。升级前务必通读 changelog.md

六、结语

Quartz.NET 不是什么新框架,它在 .NET 生态里已经存在了十几年。但正因为时间长,它的功能被磨炼得极其完善。

定时任务这件事,说简单可以很简单,说复杂也可以复杂到让你怀疑人生。

Quartz.NET 的价值在于:它把复杂的那部分替你扛了。

Cron 表达式、分布式锁、集群容错、持久化、监听链、日历排除……这些东西你自己实现,每一项都是坑。但用 Quartz.NET,你就是在搭积木。

选调度框架,就跟选数据库一样——生产环境,不要将就


群贤毕至

访客