告别重启丢失!手把手教你用Furion 4.8.8.48 + SqlSugar实现.NET 6定时任务持久化到数据库
企业级定时任务持久化实战Furion与SqlSugar的深度整合在分布式系统和企业应用中定时任务扮演着关键角色。想象一下当你的系统每天凌晨需要执行数据统计报表生成、每月初需要自动结算账单、或者每隔五分钟需要同步外部系统数据时突然遇到服务器重启或应用更新所有正在排队的任务全部消失——这种场景对业务连续性造成的破坏是灾难性的。本文将带你深入解决这一痛点通过Furion框架与SqlSugar ORM的完美结合构建一个具备状态持久化能力的企业级定时任务系统。1. 持久化架构设计原理定时任务持久化的核心目标在于确保任务状态不受应用重启影响。传统内存式任务调度器最大的缺陷就是健忘症——一旦进程终止所有调度信息灰飞烟灭。而持久化架构通过将任务元数据固化到数据库实现了任务状态的断点续传能力。Furion的持久化接口设计采用了观察者模式通过IJobPersistence接口定义了三个关键生命周期钩子public interface IJobPersistence { void OnChanged(PersistenceContext context); // 作业变更通知 void OnTriggerChanged(PersistenceTriggerContext context); // 触发器变更通知 IEnumerableSchedulerBuilder Preload(); // 初始化预加载 }这三个方法构成了持久化的黄金三角OnChanged捕获任务定义的CRUD操作OnTriggerChanged响应触发器状态的每次变化Preload系统启动时重建任务上下文数据库表设计需要同时考虑任务静态属性和运行时状态。我们采用双表结构表名核心字段用途说明JobsJobId, JobType, Concurrent存储任务基本定义JobTriggersTriggerId, Status, NextRunTime记录触发器执行状态这种分离设计符合单一职责原则也便于应对高频的状态更新操作——触发器状态的变更频率通常远高于任务定义的修改。2. 环境配置与基础搭建实现持久化的第一步是构建可靠的基础设施。我们选择SqlSugar作为数据访问层它不仅提供流畅的API还内置了高性能的批量操作支持。依赖安装需要以下NuGet包dotnet add package Furion.Pure --version 4.8.8.48 dotnet add package SqlSugarCore数据库连接配置应放在Program.cs中var builder WebApplication.CreateBuilder(args).Inject(); // 配置SqlSugar客户端 builder.Services.AddSingletonISqlSugarClient(_ new SqlSugarClient( new ConnectionConfig { ConnectionString Server.;DatabaseJobScheduler;Trusted_ConnectionTrue;, DbType DbType.SqlServer, IsAutoCloseConnection true })); // 启用Furion任务调度并注入持久化实现 builder.Services.AddSchedule(options { options.AddPersistenceDatabaseJobPersistence(); });提示生产环境建议将连接字符串移入appsettings.json并使用加密配置表结构初始化可以通过SqlSugar的CodeFirst功能自动完成// 确保表不存在时自动创建 var sqlSugar app.Services.GetRequiredServiceISqlSugarClient(); sqlSugar.CodeFirst.InitTablesJobModel, JobTriggerModel();3. 持久化核心实现持久化器的核心在于正确处理三种操作场景新增、更新和删除。我们需要将这些操作映射为对应的数据库命令。作业变更处理的典型实现public void OnChanged(PersistenceContext context) { var job JsonConvert.DeserializeObjectJobModel(context.ConvertToJSON()); switch (context.Behavior) { case PersistenceBehavior.Appended: _sqlSugar.Insertable(job).ExecuteCommand(); break; case PersistenceBehavior.Updated: _sqlSugar.Updateable(job).ExecuteCommand(); break; case PersistenceBehavior.Removed: _sqlSugar.DeleteableJobModel() .Where(x x.JobId job.JobId) .ExecuteCommand(); break; } }状态恢复逻辑是持久化的精髓所在。Preload方法需要处理两类任务静态任务通过[JobDetail]特性定义的任务动态任务通过API实时添加的任务public IEnumerableSchedulerBuilder Preload() { var dbJobs _sqlSugar.QueryableJobModel().ToList(); var dbTriggers _sqlSugar.QueryableJobTriggerModel().ToList(); // 处理静态任务 var builders App.EffectiveTypes.ScanToBuilders(); foreach (var builder in builders) { var job builder.GetJobBuilder(); var dbJob dbJobs.FirstOrDefault(x x.JobId job.JobId); if (dbJob ! null) { job.LoadFrom(dbJob); builder.Updated(); } } // 处理动态任务 foreach (var dbJob in dbJobs.Where(x !builders.Any(b b.GetJobBuilder().JobId x.JobId))) { var jobBuilder JobBuilder.Create(dbJob.JobId).LoadFrom(dbJob); var triggers dbTriggers.Where(x x.JobId dbJob.JobId) .Select(x TriggerBuilder.Create(x.TriggerId).LoadFrom(x)); builders.Add(SchedulerBuilder.Create(jobBuilder, triggers.ToArray())); } return builders; }4. 高级功能与生产实践基础持久化实现后我们需要考虑企业级应用的其他需求。动态任务管理通过控制器暴露API[DynamicApiController] public class JobController { private readonly ISchedulerFactory _scheduler; [HttpPost] public string CreateDailyJob(string jobName, string cron) { var jobId Guid.NewGuid().ToString(); _scheduler.AddJobGenericJob(jobId, Triggers.Cron(cron)); return jobId; } [HttpPost] public void PauseJob(string jobId) { _scheduler.GetJob(jobId)?.Pause(); } }失败重试机制对关键任务尤为重要// 配置任务时指定重试策略 services.AddSchedule(options { options.AddJobReportJob(Triggers.Daily(13) .SetNumRetries(3) .SetRetryTimeout(5000)); });性能优化建议为JobId和TriggerId建立数据库索引高频状态更新考虑批量提交使用内存缓存减少数据库查询// 示例批量更新触发器状态 _sqlSugar.Updateable(triggers) .WhereColumns(x new { x.TriggerId }) .ExecuteCommand();5. 监控与运维完善的监控是生产环境的必备条件。Furion内置的ScheduleUI提供了基础可视化app.UseScheduleUI(options { options.RequestPath /admin/jobs; options.DisableOnProduction false; });对于更复杂的场景可以扩展监控端点[HttpGet(api/jobs/metrics)] public JobMetrics GetMetrics() { var jobs _sqlSugar.QueryableJobModel() .Where(x !x.IsDelete) .GroupBy(x x.Status) .Select(x new { Status x.Status, Count SqlFunc.AggregateCount(x.Status) }) .ToList(); return new JobMetrics { TotalJobs jobs.Sum(x x.Count), StatusBreakdown jobs.ToDictionary(x x.Status, x x.Count) }; }定时任务持久化不是简单的技术选型问题而是系统可靠性的重要保障。通过本文介绍的模式你可以构建出能够经受生产环境考验的任务调度系统。当你的服务经历意外重启时不再需要担心任务丢失或状态混乱——系统会像什么都没发生过一样继续执行未完成的工作。