公司那套内部系统里,定时任务和后台作业加起来有二十多个。最早我全用 BackgroundService 凑合,后来遇到一个每天跑、但偶尔会失败的任务,重试逻辑写得我头疼,就陆续看了 Quartz.NET 和 Hangfire。三种都实打实用过一段时间,这里把当时怎么选的记一下,不一定对,仅供参考。
一、BackgroundService:开箱即用,但什么都要自己写
BackgroundService 是 ASP.NET Core 自带的,继承一下重写 ExecuteAsync 就能跑,不需要引入任何依赖。我们最早的数据同步任务就是这么写的,几行代码就能定时触发。但它的问题也很直接:定时靠你自己算下次执行时间,失败重试要自己写,执行记录更是一点没有。任务一旦变多,光是「这个任务上次跑成功没有」就得去翻日志,很痛苦。
这里我得承认,当时没想明白的部分是:BackgroundService 默认是在同一个进程里跑的,应用一重启任务就中断。这个坑我踩过一次——一个跑了半截的导入任务,重启之后状态就卡在中间,只能手工去把那批数据标掉重跑。
二、Quartz.NET:cron 表达式很省事,代价是配置
Quartz.NET 的 cron 表达式比自己算时间舒服太多,比如「每周一早上九点」「每月最后一天」这种,一行配置就搞定。它还支持把任务持久化到数据库,应用重启后能接着跑。我在一个需要精确到分钟级调度的场景里用过它,确实好用。
但配置成本也高:Scheduler、Job、Trigger 三件套,再加上数据库里那几张表,刚上手的时候光是把环境搭起来就花了一下午。而且它的失败重试和监控也得自己接,不算开箱即用。我们那套系统的调度规则其实没那么复杂,为了它能跑起来额外维护一套表,我觉得不划算。
三、Hangfire:我最后选了它
公司那套系统现在跑的后台任务,除了两个历史遗留的,基本都迁到了 Hangfire。选它的理由特别朴素:第一,它自带失败自动重试,我不用自己写;第二,它有一个现成的 Dashboard,能直接看到每个任务执行成功没有、失败了几次、下次什么时候跑;第三,任务可以持久化到 SQL Server,应用重启不丢。
我不是一个喜欢造轮子的人,既然有现成的面板和重试,我就不想再自己写一套。说实话,Hangfire 的免费版在功能上对我完全够用,付费的 Pro 版那些高级特性我一个都没用到,所以「值不值」这个问题对我来说不存在——免费就够用了。
四、我后来的一般选法
如果只是「应用起来后默默跑一个不重要的东西」,BackgroundService 够了;如果调度规则特别复杂、又不想引入太多东西,Quartz.NET 合适;如果任务会失败、需要重试、还想随时看执行情况,直接上 Hangfire。我们现在的经验是:能放 Hangfire 的都放 Hangfire,剩下两个只留给那些「写都写完了懒得改」的老任务。
还有一点当时纠结了挺久:Hangfire 把任务存进 SQL Server 那几张表,我一开始担心它会拖累主库。后来单独看了下,任务量就二十多个,表也就几张,IO 完全可以忽略,才放心用。如果你们的任务量是成千上万个,这块得重新评估,我这个规模下的结论不算数,别直接套。
补一句:Hangfire 的任务默认是入队执行的,如果你的任务特别重、跑得特别久,要注意工作进程的数量,不然会堵住别的任务。这个我当时是看了执行面板才发现排队变长,调了下并发数才解决。具体该设多少我没有标准答案,得看你们机器和任务本身的耗时。
下面这张表是我当时记的取舍,仅供参考:
- 定时触发:三者都能做,Quartz.NET 的 cron 表达力最强,BackgroundService 要自己算
- 长任务:都能跑,但 Hangfire 有进度和取消的概念,BackgroundService 得自己加
- 易失败重试:只有 Hangfire 是开箱即用的,另外两个都得自己写
- 执行记录:Hangfire 自带面板,另外两个基本靠翻日志