ToB企服应用市场:ToB评测及商务社交产业平台
标题:
封装 BackgroundService
[打印本页]
作者:
河曲智叟
时间:
2025-1-24 12:35
标题:
封装 BackgroundService
基类 我在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));
复制代码
另外如果有有使用 Masuit.Tools(码数吐司库) 类库的兄弟,请不要使用他的 “ASP.NET Core主动扫描注册服务” 功能,这会导致你继承BackgroundService的类莫名其妙自己启动,有Bug
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4