基类 我在ExecuteAsync中写的是while 也可以增加定时器 看自己需求,while的好处就是在上一次Work没有执行时下一次Work不会执行,定时器的话就是相反不管上一次Work有没有执行完,到下一次执行时间后,都会执行
public abstract class BaseJob : BackgroundService
{private readonly ILogger<BaseJob> logger = ServiceLocator.GetService<ILogger<BaseJob>>();public BaseJob(){Key = GetType().Name;}/// <summary>/// Key/// </summary>public string Key { get; private set; }/// <summary>/// true为启动 false为停止/// </summary>private volatile bool _isRunning;public bool IsRunning{get => _isRunning;set => _isRunning = value;}/// <summary>/// 信息/// </summary>public string? JobMsg { get; set; }/// <summary>/// 停止原因/// </summary>public string? StopMsg { get; set; }/// <summary>/// 启动时间/// </summary>public DateTime? StartTime { get; set; }/// <summary>/// 停止时间/// </summary>public DateTime? StopTime { get; set; }/// <summary>/// 备注/// </summary>public string? Remark { get; set; }/// <summary>/// 排序号/// </summary>public short OrderId { get; set; } = 1;/// <summary>/// WorkContent/// </summary>public Func<Task> WorkContent { get; set; }/// <summary>/// 是否需要轮询 默认需要/// </summary>public bool IsPoll { get; set; } = true;/// <summary>/// 轮询间隔时间 默认两秒/// </summary>public TimeSpan Delay{get => _delay;set{if (value < TimeSpan.Zero)throw new ArgumentException("延迟时间不能为负数");_delay = value;}}private TimeSpan _delay = TimeSpan.FromSeconds(2);public override async Task StartAsync(CancellationToken cancellationToken){StartTime = DateTime.Now;if (IsRunning == false){StopMsg = string.Empty;JobMsg = string.Empty;await base.StartAsync(cancellationToken);}}protected override async Task ExecuteAsync(CancellationToken stoppingToken){if (WorkContent == null) throw new ArgumentNullException(nameof(WorkContent), $"key:{Key}的Work为空");try{IsRunning = true;logger.LogInformation($"Job {Key} 开始执行");if (IsPoll){while (!stoppingToken.IsCancellationRequested){try {await WorkContent.Invoke().ConfigureAwait(false);await Task.Delay(Delay, stoppingToken).ConfigureAwait(false);stoppingToken.ThrowIfCancellationRequested();}catch (Exception ex){logger.LogError(ex, $"Job {Key} 执行出错: {ex.Message}");JobMsg = $"执行出错: {ex.Message}";}}}else{await WorkContent.Invoke();logger.LogInformation($"Job {Key} 单次执行完成");JobMsg = "单次Work已结束";}}catch (Exception ex){JobMsg = "异常:" + ex.Message;await StopAsync(stoppingToken, $"BaseJob捕获到异常已停止:{ex.Message}");}finally{IsRunning = false;// 确保 IsRunning 在方法退出时设置为 false}}public virtual async Task StopAsync(CancellationToken cancellationToken, string stopMsg){logger.LogError($"Job {Key} 退出");StopMsg = stopMsg;StopTime = DateTime.Now;IsRunning = false;await base.StopAsync(cancellationToken);}
}
最基本的使用案例 会在控制台两秒输出一次
public class TestJob : BaseJob
{public TestJob(){base.Remark = "测试Job";base.WorkContent = () =>{Console.WriteLine("Test");return Task.CompletedTask;};}
}
另外是对Job的管理查询,启动和停止
public class JobManager
{private readonly IServiceProvider _serviceProvider;private readonly List<JobInfo> _jobs = new();public JobManager(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}public void AddJob<T>() where T : BaseJob{//ActivatorUtilities.CreateInstance 方法接受以一个参数://_serviceProvider:这是 IServiceProvider 对象,它提供了创建服务实例的能力。//因为已经给T了 所以不需要第二个参数了 而且类也是无参的构造函数var job = ActivatorUtilities.CreateInstance<T>(_serviceProvider);_jobs.Add(new JobInfo(job));}public void AddJob(Type jobType){//ActivatorUtilities.CreateInstance 方法接受三个参数://_serviceProvider:这是 IServiceProvider 对象,它提供了创建服务实例的能力。//第二个参数:这是 Type 对象,表示您想要创建的类的类型。//第三个参数:这个参数在创建实例时被传递给构造函数。var job = (BaseJob)ActivatorUtilities.CreateInstance(_serviceProvider, jobType);_jobs.Add(new JobInfo(job));}public async Task StartJobAsync(string key, CancellationToken cancellationToken){var job = GetJobInfo(key);if (job != null) await job.Service.StartAsync(cancellationToken);}public async Task StartJobAllAsync(CancellationToken cancellationToken){foreach (var job in _jobs){if (job.IsRunning) continue;try{await job.Service.StartAsync(cancellationToken);}catch (Exception ex){LogHelp.LogError($"{job.Key}启动异常{ex.Message}");}}}public async Task StopJobAsync(string key, CancellationToken cancellationToken){var job = GetJobInfo(key);if (job != null) await job.Service.StopAsync(cancellationToken, "手动停止");}public async Task StopJobAllAsync(CancellationToken cancellationToken){foreach (var job in _jobs) await job.Service.StopAsync(cancellationToken, "手动停止");}public IEnumerable<JobInfo> GetJobInfos(){return _jobs.OrderBy(s=>s.Service.OrderId);}public JobInfo GetJobInfo(string key){return _jobs.FirstOrDefault(j => j.Key == key);}
}
public static class JobExtension
{/// <summary>/// 注入JobManager/// </summary>/// <param name="services"></param>public static void AddJobManager(this IServiceCollection services){services.AddSingleton<JobManager>();var assemblies = AppDomain.CurrentDomain.GetAssemblies();var jobTypes = assemblies.SelectMany(a => a.GetTypes()).Where(t => typeof(BaseJob).IsAssignableFrom(t) && !t.IsAbstract);foreach (var jobType in jobTypes){services.AddSingleton(jobType);}}/// <summary>/// 添加所有Job 默认启动/// </summary>/// <param name="app"></param>/// <param name="noStartJob">不启动哪些Job</param>/// <param name="IsStart">是否启动 默认为true</param>/// <returns></returns>public static void UseAddJobAll(this IApplicationBuilder app, bool isStart = true, params string[] noStartJob){// 获取所有继承自 BaseJob 的子类类型var jobTypes = AppDomain.CurrentDomain.GetDerivedTypes<BaseJob>();// 获取 JobManager 实例var jobManager = app.ApplicationServices.GetRequiredService<JobManager>();foreach (var jobType in jobTypes){if (noStartJob.Contains(jobType.Name)) continue;jobManager.AddJob(jobType);}if (isStart) jobManager.StartJobAllAsync(CancellationToken.None).GetAwaiter();}/// <summary>/// 添加所有Job 默认启动/// </summary>/// <param name="app"></param>/// <param name="IsStart">是否启动 默认为true</param>/// <returns></returns>public static void UseAddJobAll(this IApplicationBuilder app, bool isStart = true){// 获取所有继承自 BaseJob 的子类类型var jobTypes = AppDomain.CurrentDomain.GetDerivedTypes<BaseJob>();// 获取 JobManager 实例var jobManager = app.ApplicationServices.GetRequiredService<JobManager>();foreach (var jobType in jobTypes) jobManager.AddJob(jobType);if (isStart) jobManager.StartJobAllAsync(CancellationToken.None).GetAwaiter();}private static IEnumerable<Type> GetDerivedTypes<T>(this AppDomain appDomain){return appDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes()).Where(type => typeof(T).IsAssignableFrom(type) && !type.IsAbstract && type != typeof(T));}
}
在WebApi项目Program类中来调用方法
builder.Services.AddJobManager();
app.UseAddJobAll(false,nameof(S1StackerErrorJob), nameof(S2StackerErrorJob));