Longbow.Tasks
Longbow.Tasks
概述
大体分为了Scheduler(调度任务),Storage(持久化),Trigger(触发器),Task(任务)和逻辑模块,大体流程为通过逻辑代码进行实例化相关类,根据ITask来实现独立的业务流程(也可以使用自带业务类),通过配置触发器来设定触发时间,使用持久化来防止程序崩溃导致任务消失,通过调度任务执行任务。
Scheduler
包含了任务调度器的枚举(准备,运行,禁用),调度器类的接口,内部默认实现类和调度执行的实体类
-
IScheduler(任务调度类接口)
- 包含了调度器状态,下次与您时间,上次执行时间,上次执行结果,调度器创建时间,相关触发器,相关的任务信息
-
DefaultScheduler(内部默认任务调度类)
-
Triggers(相关触发器):SchedulerProcess
-
NextRuntime(下次运行时间):Triggers中可用的最小的运行时间
-
LastRuntime(最近运行时间):Triggers中最后一次运行时间的最大值
SchedulerProcess?.Triggers.Select(t => t.Trigger.LastRuntime).Max()
-
CreatedTime (创建时间) :DateTimeOffset.Now
-
-
Start和Stop方法--开始/停止任务调用
- 该方法是在将调度处理器设置为Running时调用
-
SchedulerProcess(调度执行实体类)
-
包含了日志委托,任务调度器,调度任务和所有触发器实例
-
Start :调度开始
public void Start(ITask task, ITrigger trigger){ Task.Run(() => { // 获得任务或 where T:ITask,new()的泛型 // DefaultTaskMetaData? TaskContext = new DefaultTaskMetaData(task); DefaultTaskMetaData? TaskContext = new DefaultTaskMetaData(new T()); // 将任务赋值给调度器 _sche.Task = TaskContext.Task; _initToken.Cancel(); // Stop 调用 if (_cancellationTokenSource?.IsCancellationRequested ?? false) return; }); // 设置超时取消 // 调用Task任务 // 声明触发器,并注册回调函数开始触发器执行 InternalStart(trigger); } // 执行Task并开始触发器 private void InternalStart(ITrigger trigger) { //声明一个返回值为Task的,参数为CancellationToken的Func var dowork = new Func<CancellationToken, Task>(async token => { //没有异常信息 _sche.Exception = null; // 设置任务超时取消令牌 var taskCancelTokenSource = new CancellationTokenSource(trigger.Timeout); try { // 保证 ITask 的 new() 方法被执行完毕,泛型中有声明 _initToken.Token.WaitHandle.WaitOne(); var taskToken = CancellationTokenSource.CreateLinkedTokenSource(token, taskCancelTokenSource.Token); if (!taskToken.IsCancellationRequested && TaskContext != null) { //执行异步任务 await TaskContext.Execute(taskToken.Token).ConfigureAwait(false); //异步任务执行完毕,设置为成功 trigger.LastResult = TriggerResult.Success; } } catch (TaskCanceledException) { } catch (Exception ex) { //有异常,保存异常 _sche.Exception = ex; ex.Log(); } // 设置 Trigger 状态 // 取消 if (token.IsCancellationRequested) trigger.LastResult = TriggerResult.Cancelled; // 超时 if (taskCancelTokenSource.IsCancellationRequested) trigger.LastResult = TriggerResult.Timeout; // 错误 if (_sche.Exception != null) trigger.LastResult = TriggerResult.Error; }); // 声明触发器的维护类 var triggerProcess = new TriggerProcess(Scheduler.Name, LoggerAction, trigger, Storage, dowork); // 保存到触发器数组中 Triggers.Add(triggerProcess); // 注册触发器状态改变回调方法 trigger.EnabeldChanged = enabled => { // 当触发器状态改变后进行操作 LoggerAction($"{nameof(TriggerProcess)} Trigger({trigger.GetType().Name}) Enabled({enabled})"); if (!enabled) { // 触发器设置为禁用,则持久化的任务设置为停止 triggerProcess.Stop(); return; } // 状态为运行时,则继续运行触发器的维护类(让触发器开始工作) if (Status == SchedulerStatus.Running) triggerProcess.Start(_cancellationTokenSource?.Token); }; // 当当前状态为就绪 if (Status == SchedulerStatus.Ready) { // 设置为运行 Status = SchedulerStatus.Running; _cancellationTokenSource = new CancellationTokenSource(); // 运行触发器的维护类 triggerProcess.Start(_cancellationTokenSource.Token); } }
-
Storage
持久化相关类,包含了持久化到文件,序列化触发器,加密等。主要流程为将已初始化的触发器经过加密设置(密钥和向量值)成序列化值,然后将序列化的信息保存到本地文件中的操作
-
FileStorageOptions
持久化配置信息:是否物理文件持久化(默认为true);保存的文件目录;是否加密(默认true);密钥;向量值
-
IStorage 持久化接口
/// 从持久化载体加载 ITrigger 触发器 Task LoadAsync(); /// 将 ITrigger 触发器保存到序列化载体中 /// <param name="schedulerName">任务调度器名称</param> /// <param name="trigger"></param> bool Save(string schedulerName, ITrigger trigger); /// 获得 上一次操作异常信息实例 Exception? Exception { get; } /// 移除指定任务调度 /// <param name="schedulerNames">要移除调度名称集合</param> bool Remove(IEnumerable<string> schedulerNames);
-
FileStorage 持久化到物理文件操作类
主要方法参数说明
public virtual Task LoadAsync() { // 从文件加载 Exception = null; // 物理持久化为true时 if (Options.Enabled){ //获得配置中保存的路径,并获得所有*.bin的文件进行遍历 RetrieveSchedulers().AsParallel().ForAll(fileName =>{ // 如果存在 if (File.Exists(fileName)){ try{ lock (locker){ //创建任务名称 var scheduleName = Path.GetFileNameWithoutExtension(fileName); // 反序列化,将string转换为触发器 var trigger = JsonSerializeExtensions.Deserialize(fileName, Options); //获得任务类 // 返回为null???很奇怪 var task = CreateTaskByScheduleName(scheduleName); if (task != null){ TaskServicesManager.GetOrAdd(scheduleName, task, trigger); }else{ // 返回为null???很奇怪 var callback = CreateCallbackByScheduleName(scheduleName); if (callback != null){ TaskServicesManager.GetOrAdd(scheduleName, callback, trigger); } } } } catch (Exception ex) { Exception = ex; // load 失败删除文件防止一直 load 出错 var target = $"{fileName}.err"; if (File.Exists(target)) File.Delete(target); File.Move(fileName, $"{fileName}.err"); } } }); } return Task.CompletedTask; }
-
FileStorageExtensions
- ITaskStorageBuilder的扩展类,用于注入物理文件持久化服务到容器
-
JsonSerializeExtensions 二进制序列化操作类
-
使用TripleDES加密方式
// 解密 private static string Decrypte(this string data, FileStorageOptions option) { using var des = Create(option.Key, option.IV); // 创建解密者 var decryptor = des.CreateDecryptor(); // 将String转换为byte数组 var buffer = Convert.FromBase64String(data); // 解密数组块 var result = decryptor.TransformFinalBlock(buffer, 0, buffer.Length); return Encoding.UTF8.GetString(result); } // 加密 private static string Encrypte(this string data, FileStorageOptions option) { using var des = Create(option.Key, option.IV); //创建加密者 var encryptor = des.CreateEncryptor(); //转换 var buffer = Encoding.UTF8.GetBytes(data); // 加密 var result = encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return Convert.ToBase64String(result); } // 创建TripleDES private static TripleDES Create(string key, string iv) { var des = TripleDES.Create(); //设置加密模式 des.Mode = CipherMode.ECB; // 填充算法 des.Padding = PaddingMode.PKCS7; // 向量值 des.IV = Convert.FromBase64String(iv); // 加密值 des.Key = Convert.FromBase64String(key); return des; }
-
Trigger
- ITrigger 触发器接口
- Cron Cron操作类
- DefaultTrigger 内部默认的触发器,只执行一次
- CronTrigger Cron触发器
- 通过心跳来判断下次执行时间
- CronExpression.GetNextExecution() 获得下次执行时间
- RecurringTrigger 重复执行的触发器
- 通过心跳来判断下次执行时间
- 等待一个间隔周期,进行触发
Task
- ITask 任务类接口
逻辑模块
-
TaskServiceCollectionExtensions
- IServiceCollection注入方法
-
TaskServicesFactory
- Create
- StartAsync:调用物理持久化的加载项
- StopAsync:调用了TaskServicesManager.Shutdown(token)方法
-
TaskServicesManager
-
Init(初始化)
// 创建任务工厂 TaskServicesFactory.Create(options, storage ?? new NoneStorage()); // 加载物理持久化中的触发器 _ = Factory?.StartAsync();
-
GetOrAdd(新增)
internal static readonly ConcurrentDictionary<string, Lazy<SchedulerProcess>> _schedulerPool = new(); // 将任务与触发器添加到调度中 多线程安全 public static IScheduler GetOrAdd<T>(string schedulerName, ITrigger? trigger = null) where T : ITask, new() { // 判断任务名称,如果不存在则直接使用任务名称 if (string.IsNullOrEmpty(schedulerName)) { schedulerName = typeof(T).Name; } // 懒加载,因为内部方法中有并发任务,因此需要用懒加载 return _schedulerPool.GetOrAdd(schedulerName, key => new Lazy<SchedulerProcess>(() => { // 创建调度器,并将创建的调度器与任务调度器关联 var process = GetSchedulerProcess(key); // 绑定任务与触发器 process.Start<T>(trigger ?? TriggerBuilder.Default.Build()); return process; })).Value.Scheduler; }
-