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;
}
Longbow.Tasks的更多相关文章
- .NET Core 实现后台任务(定时任务)Longbow.Tasks 组件(三)
原文链接:https://www.cnblogs.com/ysmc/p/16512309.html 在上两篇文章中,简单介绍了怎么使用 IHostedService 与 BackgroundServi ...
- .Net多线程编程—System.Threading.Tasks.Parallel
System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Parallel.For,Parallel.ForEach这三个静态方法. 1 Parallel. ...
- 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 ...
- include/linux/tasks.h
#ifndef _LINUX_TASKS_H#define _LINUX_TASKS_H /* * This is the maximum nr of tasks - change it if you ...
- 双核CPU,跑程序会报rcu_sched_state detected stalls on CPUs/tasks 错误
有一份SDK,之前跑在PPC405EX上没问题。最近换平台,CPU使用了PowerPC的P1020,双核。linux版本也升级到了3.0.48版本。升级之后出现了一个问题:SDK里面的程序跑一段时间之 ...
- JavaScript tasks, microtasks, queues and schedules
最近做的项目中,涉及到了JavaScript中Promise的用法,于是做了一点测试,发现没有想象中的那么简单,水很深,所以找来N先生(我的Mentor),想得到专业的指导.N先生也不尽知,但N先生查 ...
- vscode中启动浏览器的tasks.json
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tas ...
- Threading.Tasks 简单的使用
using Lemon.Common; using System; using System.Collections.Generic; using System.Linq; using System. ...
- 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 ...
- Tasks.Parallel
.Net多线程编程-System.Threading.Tasks.Parallel System.Threading.Tasks.Parallel类提供了Parallel.Invoke,Paral ...
随机推荐
- pod(五):pod hook(pod钩子)和优雅的关闭nginx pod
目录 一.系统环境 二.前言 三.pod hook(pod钩子) 四.如何优雅的关闭nginx pod 一.系统环境 服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架 ...
- 使用jmx exporter采集kafka指标
预置条件 安装kafka.prometheus 使用JMX exporter暴露指标 下载jmx exporter以及配置文件.Jmx exporter中包含了kafka各个组件的指标,如server ...
- webpack中 hash chunkhash
hash一般是结合CDN缓存来使用,通过webpack构建之后,生成对应文件名自动带上对应的MD5值.如果文件内容发生改变的话,那么对应文件hash值也会改变,对应的HTML引用的URL地址也会改变, ...
- Java自定义排序
实现Comparator接口 实现该接口需要重写compare()方法 Arrays.sort(students, new Comparator<Student>() { @Overrid ...
- 单节点部署 gpmall 商城
个人名片: 对人间的热爱与歌颂,可抵岁月冗长 Github:念舒_C.ying CSDN主页️:念舒_C.ying 个人博客 :念舒_C.ying 1 修改主机名: [root@localhost ...
- 如何使用zx编写shell脚本
前言 在这篇文章中,我们将学习谷歌的zx库提供了什么,以及我们如何使用它来用Node.js编写shell脚本.然后,我们将学习如何通过构建一个命令行工具来使用zx的功能,帮助我们为新的Node.js项 ...
- windows安装grunt时提示不是内部或外部命令解决方案
参考:https://www.cnblogs.com/hts-technology/p/8477258.html 安装windows安装elasticsearch-head时 不需要输入grunt s ...
- 【CDH数仓】Day02:业务数仓搭建、Kerberos安全认证+Sentry权限管理、集群性能测试及资源管理、邮件报警、数据备份、节点添加删除、CDH的卸载
五.业务数仓搭建 1.业务数据生成 建库建表gmall 需求:生成日期2019年2月10日数据.订单1000个.用户200个.商品sku300个.删除原始数据. CALL init_data('201 ...
- go-carbon 1.5.0 版本发布,修复已知 bug 和新增德语翻译文件
carbon 是一个轻量级.语义化.对开发者友好的golang时间处理库,支持链式调用. 目前已被 awesome-go 收录,如果您觉得不错,请给个star吧 github:github.com/g ...
- 如何查看计算机的CPU信息
CPU-Z是一款家喻户晓的CPU检测软件,是检测CPU使用程度极高的一款软件.它支持的CPU种类相当全面,软件的启动速度及检测速度都很快.另外,它还能检测主板和内存的相关信息,其中就有我们常用的内存双 ...