需求描述:我在使用ABP框架,现在想实现一个定时任务功能,描述:每天八点调用特定接口,调用n次后结束不再调用。相关的数据都来自于一个特定的实体“fuck”。
解决方案:可以使用ABP框架自带的定时任务功能来实现
解决步骤:
public class FuckJob : BackgroundJob
{
private readonly IRepository _fuckRepository;
public FuckJob(IRepository fuckRepository)
{
_fuckRepository = fuckRepository;
}
public override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// 获取 "fuck" 实体
var fuck = await _fuckRepository.FirstOrDefaultAsync();
// 调用特定接口的逻辑
// ...
// 更新 "fuck" 实体的相关数据
fuck.SomeProperty = someValue;
await _fuckRepository.UpdateAsync(fuck);
}
}
[DependsOn(
typeof(AbpBackgroundJobsModule),
typeof(YourProjectNameEntityFrameworkCoreModule)
)]
public class YourProjectNameWebHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// 注册 "FuckJob" 类
context.Services.AddTransient();
Configure(options =>
{
options.AddJob(job => job
.WithInterval(TimeSpan.FromDays(1)) // 每天执行一次
.WithDescription("每天八点调用特定接口,调用n次后结束不再调用")
.WithTolerateTime(TimeSpan.FromMinutes(30)) // 允许任务延迟30分钟
.WithRunTimes(10) // 执行10次后结束
.Build());
});
}
}
public class YourProjectNameWebHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient(provider =>
{
var fuckRepository = provider.GetService>();
// 获取 "fuck" 实体中的数据
var fuck = fuckRepository.FirstOrDefault();
var executeTimes = fuck.ExecuteTimes;
var maxRunTimes = fuck.MaxRunTimes;
return new FuckJob(fuckRepository, executeTimes, maxRunTimes);
});
Configure(options =>
{
options.AddJob(job => job
.WithInterval(TimeSpan.FromDays(1)) // 每天执行一次
.WithDescription("每天八点调用特定接口,调用n次后结束不再调用")
.WithTolerateTime(TimeSpan.FromMinutes(30)) // 允许任务延迟30分钟
.WithRunTimes(maxRunTimes) // 执行 n 次后结束
.Build());
});
}
}
实际业务需求中,有一个开关来添加和删除后台任务。
翻译为技术需求:在启动类之外的地方AddJob
返回值为添加的job的Guid
应该有另外一张表来存储这些后台任务
public class YourController : YourProjectNameControllerBase
{
private readonly IBackgroundJobManager _backgroundJobManager;
public YourController(IBackgroundJobManager backgroundJobManager)
{
_backgroundJobManager = backgroundJobManager;
}
public async Task AddFuckJob()
{
// 获取 "fuck" 实体中的数据
var fuck = await _fuckRepository.FirstOrDefaultAsync();
var executeTimes = fuck.ExecuteTimes;
var maxRunTimes = fuck.MaxRunTimes;
// 添加后台任务
await _backgroundJobManager.AddJobAsync(
new JobInvocation(
typeof(FuckJob),
new JobArgs
{
Args = new object[] { executeTimes, maxRunTimes }
}
)
);
// 返回任务标识符
return Ok(jobId);
}
}
public class YourController : YourProjectNameControllerBase
{
private readonly IBackgroundJobManager _backgroundJobManager;
public YourController(IBackgroundJobManager backgroundJobManager)
{
_backgroundJobManager = backgroundJobManager;
}
public async Task RemoveFuckJob()
{
// 删除后台任务
await _backgroundJobManager.RemoveAsync(typeof(FuckJob));
return Ok();
}
}
在application层 推送一个eto出去,ILocalEventHandler内去触发一个定时任务,那么这个定时任务的逻辑被定义在domian层(manager结尾的文件)。
domain层处理backgroundJob,多个接口相关后台任务的增加删除。
那么数据源独自一张单独的表,每次触发开关时候要做两件事
1. 修改数据源实体
2. 删除/增加 typeof(xxxx)的backgroundJob
我的实现方式是使用 ABP 框架提供的后台任务管理功能,而 Quartz 是一个独立的任务调度框架。它们的区别主要有以下几点:
综上所述,ABP 框架的后台任务管理功能适用于一些简单的任务调度场景,而 Quartz 则适用于更加复杂和灵活的任务调度场景。但是,在使用 Quartz 时需要额外进行配置和学习成本比较高,而 ABP 框架的后台任务管理功能则是直接集成在框架中,使用起来比较方便。
好的,下面是使用 Quartz 实现该需求的步骤:
打开 Visual Studio,右键点击项目名称,选择“管理 NuGet 程序包”,在“浏览”选项卡中搜索“Quartz”,选择“Quartz”并安装。
创建一个继承自 `IJob` 接口的 Job 类,并实现 `Execute` 方法。在该方法中编写需要执行的业务逻辑。
public class FuckJob : IJob
{
private readonly IFuckRepository _fuckRepository;
public FuckJob(IFuckRepository fuckRepository)
{
_fuckRepository = fuckRepository;
}
public async Task Execute(IJobExecutionContext context)
{
// 获取 "fuck" 实体中的数据
var fuck = await _fuckRepository.FirstOrDefaultAsync();
var executeTimes = fuck.ExecuteTimes;
var maxRunTimes = fuck.MaxRunTimes;
// 执行业务逻辑
if (executeTimes < maxRunTimes)
{
// TODO: 执行业务逻辑
// 更新 "fuck" 实体中的数据
fuck.ExecuteTimes++;
await _fuckRepository.UpdateAsync(fuck);
}
}
}
在 `Startup.cs` 文件中,使用 `JobBuilder` 类来配置 JobDetail。其中,`WithIdentity` 方法用于设置 Job 的标识符,`OfType` 方法用于设置 Job 的类型,`Build` 方法用于创建 JobDetail。
public void ConfigureServices(IServiceCollection services)
{
// 注册 IFuckRepository 和 IBackgroundJobManager
services.AddScoped();
services.AddScoped();
// 配置 Quartz
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionScopedJobFactory();
q.AddJob(j => j
.WithIdentity("FuckJob")
.OfType()
);
});
services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
// 其他配置...
}
使用 `TriggerBuilder` 类来配置 Trigger。其中,`WithIdentity` 方法用于设置 Trigger 的标识符,`StartNow` 方法用于设置 Trigger 的起始时间,`WithSimpleSchedule` 方法用于设置 Trigger 的调度方式,`Build` 方法用于创建 Trigger。
public void ConfigureServices(IServiceCollection services)
{
// 注册 IFuckRepository 和 IBackgroundJobManager
services.AddScoped();
services.AddScoped();
// 配置 Quartz
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionScopedJobFactory();
q.AddJob(j => j
.WithIdentity("FuckJob")
.OfType()
);
q.AddTrigger(t => t
.WithIdentity("FuckTrigger")
.StartNow()
.WithSimpleSchedule(s => s
.WithIntervalInSeconds(10)
.RepeatForever()
)
.ForJob("FuckJob")
);
});
services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
// 其他配置...
}
在上述代码中,我使用了 `WithIntervalInSeconds(10)` 方法来设置任务调度的时间间隔为 10 秒,即每隔 10 秒执行一次任务。
在 `Program.cs` 文件中,启动任务调度。
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
})
.UseQuartz();
在使用 Quartz 时,还可以通过 `IScheduler` 接口来操作任务调度。例如,可以使用 `scheduler.PauseAll()` 方法来暂停所有任务调度,使用 `scheduler.ResumeAll()` 方法来恢复所有任务调度。
public class YourController : YourProjectNameControllerBase
{
private readonly IScheduler _scheduler;
public YourController(IScheduler scheduler)
{
_scheduler = scheduler;
}
public async Task PauseAllJobs()
{
// 暂停所有任务调度
await _scheduler.PauseAll();
return Ok();
}
public async Task ResumeAllJobs()
{
// 恢复所有任务调度
await _scheduler.ResumeAll();
return Ok();
}
}
上述代码中的 `YourProjectNameControllerBase` 是你的项目中定义控制器的基类,需要根据实际情况进行修改。
https://monica.im/s/2e53179e?locale=enhttps://monica.im/s/2e53179e?locale=en