Quartz.NET企业级应用必须解决持久化、集群、Misfire、线程争用和动态管理问题:AdoJobStore需手动配置连接池与索引,Misfire策略须按业务选型,动态任务须复用单例IScheduler,集群中instanceId必须全局唯一。
在企业级场景中不是“配好就能跑”,而是必须直面持久化、集群、Misfire、线程争用和动态管理这些真实问题。生产环境直接用
启动等于裸奔——重启即丢任务,单点故障无冗余,高并发下触发延迟不可控。
AdoJobStore 必须显式配置连接池与索引
用 SQL Server 或 PostgreSQL 做持久化时,
默认不启用连接池,也不建索引,高频调度下很容易卡在数据库查询上。
至少设为 10,避免触发器获取阶段排队等待
必须手动为
表的
和
字段加联合索引,否则每秒上百触发器时
变全表扫描
定期清理
表(如每天凌晨 truncate),否则日志膨胀拖慢整个 JobStore
CronTrigger 的 Misfire 处理策略不能依赖默认值
网络抖动、GC 暂停、数据库锁表都可能导致触发时间错过。Cron 表达式本身不定义“错过怎么办”,全靠
控制行为。
每 5 分钟执行一次,若服务停了 20 分钟,重启后默认只执行最后一次错过的,前 3 次被丢弃
改用
才会把所有错过的全部补跑(适合报表生成类任务)
对支付对账类任务,反而要用
防止重复扣款
动态添加 Job 时必须复用同一个 IScheduler 实例
常见错误是每次新增任务都 new 一个
,结果出现多个调度器实例竞争同一张数据库表,引发死锁或触发器丢失。
C知道
CSDN推出的一款AI技术问答工具
下载
ASP.NET Core 中应通过 DI 获取单例
:用
动态注册 Job 必须调用
,否则同名 Job 会报
修改 Trigger 时不要删再建,用
保证原子性
集群模式下 Scheduler ID 和 Instance ID 必须全局唯一
多节点部署时,如果两个实例用了相同的
,它们会互相踢出对方,表现为任务随机中断、日志里反复出现
。
绝对不要写死
—— AUTO 在容器环境下常生成重复 ID
推荐用主机名 + 进程 PID 拼接,如
(需在启动时注入环境变量)
可共用(如都叫
),但
必须每个进程不同
真正难的从来不是“怎么让任务跑起来”,而是“怎么让任务在数据库挂过、机器重启、流量突增、配置误改之后,依然按你预期的时间和次数执行”。这些细节不提前压测、不看日志、不查表状态,上线后只会以
数据错乱或
卡在
的形式悄悄爆发。
Quartz.NETUseInMemoryStore()AdoJobStorequartz.dataSource.myDS.maxConnectionsQRTZ_TRIGGERSNEXT_FIRE_TIMESTATESELECTQRTZ_FIRED_TRIGGERSMisfireInstructionWithCronSchedule("0 0/5 * * * ?").WithMisfireHandlingInstructionFireAndProceed().WithMisfireHandlingInstructionDoNothing()StdSchedulerFactory().GetScheduler()ISchedulerservices.AddQuartz(q => { ... }).AddQuartzHostedService(...)await scheduler.AddJob(job, replace: true)ObjectAlreadyExistsExceptionawait scheduler.RescheduleJob(triggerKey, newTrigger)quartz.scheduler.instanceIdClusterManager: detected cluster failurequartz.scheduler.instanceId = AUTOquartz.scheduler.instanceId = ${HOSTNAME}-${PROCESS_ID}quartz.scheduler.instanceNamepayment-schedulerinstanceIdQRTZ_JOB_DETAILSTriggerStatePAUSED