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;
      }

Longbow.Tasks的更多相关文章

  1. .NET Core 实现后台任务(定时任务)Longbow.Tasks 组件(三)

    原文链接:https://www.cnblogs.com/ysmc/p/16512309.html 在上两篇文章中,简单介绍了怎么使用 IHostedService 与 BackgroundServi ...

  2. .Net多线程编程—System.Threading.Tasks.Parallel

    System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Parallel.For,Parallel.ForEach这三个静态方法. 1 Parallel. ...

  3. spark - tasks is bigger than spark.driver.maxResultSize

    Error ERROR TaskSetManager: Total size of serialized results of 8113 tasks (1131.0 MB) is bigger tha ...

  4. include/linux/tasks.h

    #ifndef _LINUX_TASKS_H#define _LINUX_TASKS_H /* * This is the maximum nr of tasks - change it if you ...

  5. 双核CPU,跑程序会报rcu_sched_state detected stalls on CPUs/tasks 错误

    有一份SDK,之前跑在PPC405EX上没问题。最近换平台,CPU使用了PowerPC的P1020,双核。linux版本也升级到了3.0.48版本。升级之后出现了一个问题:SDK里面的程序跑一段时间之 ...

  6. JavaScript tasks, microtasks, queues and schedules

    最近做的项目中,涉及到了JavaScript中Promise的用法,于是做了一点测试,发现没有想象中的那么简单,水很深,所以找来N先生(我的Mentor),想得到专业的指导.N先生也不尽知,但N先生查 ...

  7. vscode中启动浏览器的tasks.json

    {    // See https://go.microsoft.com/fwlink/?LinkId=733558    // for the documentation about the tas ...

  8. Threading.Tasks 简单的使用

    using Lemon.Common; using System; using System.Collections.Generic; using System.Linq; using System. ...

  9. Effective Java 68 Prefer executors and tasks to threads

    Principle The general mechanism for executing tasks is the executor service. If you think in terms o ...

  10. Tasks.Parallel

    .Net多线程编程-System.Threading.Tasks.Parallel   System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Paral ...

随机推荐

  1. 我用EasyExcel优化了公司的导出(附踩坑记录)

    背景介绍 最近要改一个导出的功能,在原有的基础上,在导出一份明细数据,要求导出内容加在原有 excel 的第二个 sheet 上.考虑到数据量还比较大,干脆引入阿里的 EasyExcel 来做. 下面 ...

  2. JavaWeb2

    1. web服务器软件:Tomcat 1.1 概述 服务器:安装了服务器软件的计算机 服务器软件:接受用户的请求,处理请求,做出响应 web服务器软件:接受用户的请求,处理请求,做出响应 在web服务 ...

  3. python学习——查找计算机中的文件

    # import os # # path = 'C:/Users/admin/Desktop/images' # files = os.listdir(path) # # for f in files ...

  4. 【DL论文精读笔记】VGGNet

    VGGNet(Very Deep Convolutional Networks) 1.introduction ● 采用3x3的小卷积核应用在比较深的网络里 ● 结果不错,赢得了2014 Imagen ...

  5. SpringCloud(十一)- 秒杀 抢购

    1.流程图 1.1 数据预热 1.2 抢购 1.3 生成订单 (发送订单消息) 1.4 订单入库 (监听 消费订单消息) 1.5 查看订单状态 1.6 支付 (获取支付链接 ) 1.7 支付成功 微信 ...

  6. 关于仿照js写python算法

    前言 我们学校的统一认证的登录系统,用了一套不知道哪弄来的 js加密算法 (我已经查到了,应该是出自这里 地址),有一个参数是通过 js 计算得到的,无奈我先想模拟登录就必须解决这个算法,这个说明是d ...

  7. 【PPT】NET Conf China 2022,主题:C#在iNeuOS工业互联网操作系统的开发及应用

    从技术生态发展过程及理念.产品级解决方案理念.产品系统框架及主要功能介绍.产品系统二次开发和应用案例等5个方面进行了主题发言. 从2003到现在,使用.NET技术生态19年左右.  10多年的煤炭.电 ...

  8. 【每日一题】【归并排序/堆排序&虚拟头结点】148. 排序链表-211220/220217【出栈时不断容易产生环状链表!】

    给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 . 进阶: 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗? 方法1:归并排序+使用辅助函数 ...

  9. 一文理解什么是DevOps,通俗易懂白话文

    一文理解什么是DevOps,通俗易懂白话文 devops是什么❝DevOps维基百科定义 DevOps(Development和Operations的组合词)是一种重视"软件开发人员(Dev ...

  10. MySQL进阶实战7,查询的执行过程

    @ 目录 一.拆分查询 二.分解关联查询 三.查询的执行过程 四.优化器的一些优化手段 1.重新定义关联表的顺序 2.将外连接转化为内连接 3.使用增加变换规则 4.优化count().max().m ...