[开源内卷] .NET 定时任务 -- FreeScheduler 支持 cron、持久化、可变定时设置
前言
卷了,卷了,卷了,最近太卷。。。这篇文章写了好几天了,由于同类型文章太多,排期到今天发布。实在不想卷,得罪了!各位定时任务开源大佬们!
.NET 定时组件生态实在太强大了,写下此文只希望能供大家多一个选择,不想重复造轮子,实在是事出有因。
高中读书那会,盛大传奇是最火爆的网络游戏,我和我的同学们都对它有过沉迷,甚至到上班几年之后,对它仍然有一种莫名的情怀。
干我们这行忙的时候要加班,闲的时候也很闲,在我曾经很闲的一份工作里,为了情怀去研究了传奇sf引擎,在简洁的脚本代码里我发现了一个宝藏:活动定时任务。除了以秒单位定时触发,还可以设置每月某天、每周某天、每天某时间,在 .net framework 3.0 普遍还在使用 Timer 的年代,我一下子被惊艳到了,于是利用 Timer 仿着功能自己实现了一版 .NET 定时任务功能类。
一开始只是一个类直接放进项目内使用,从未发布过 nuget 版本。打从 2016 年接触 .net core 以来,励志为开源生涯添砖加瓦,这才有了正式发布的念头。我曾经维护过 csredis(因原作者不维护所以发布为 CSRedisCore),呕心沥血从零到一开源 FreeSql,重构 RedisClient 发布的 FreeRedis,聊天架构 IMCore。。。等等。
因 FreeSql 使用需求编写了有趣的开源组件 IdleBus,写完后发现它的特点还蛮适合用来扩展定时任务,于时重构了一个版本命名 IdleScheduler,在 2020 年发布开源,前不久已正式改名为 FreeScheduler。
经历了十几年的使用需求和改造进化,实在是"食之无味,弃之可惜"。还是供大家多一个选择吧!
主要优势
FreeScheduler 轻量化定时任务调度,支持临时的延时任务和重复循环任务(可持久化),可按秒,每天/每周/每月固定时间,自定义间隔执行(CRON表达式),支持 .NET Framework 4.0,.NETCore2.1 +,Xamrin、MAUI 等平台 运行环境。
特色功能之一:FreeScheduler 支持一个任务设置 [5,5,30,30,60] 不同的定时间隔,任何一次成功都可结束整个任务。
scheduler.AddTask("比武大会", "json", new [] { 5, 5, 30, 30, 60 });
class MyTaskHandler : FreeScheduler.TaskHandlers.TestHandler
{
public override void OnExecuting(Scheduler scheduler, TaskInfo task)
{
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] {task.Topic} 被执行");
if (task.Topic == "比武大会")
{
try
{
//todo..
//任何一次不报错,强制使任务完成
task.Status = TaskStatus.Completed;
}
finaly
{
}
}
}
}
轻量化解释:了解 FreeRedis、FreeSql、csredis 的人都知道,我们发布的开源项目是绿色著称,零依赖发布后只有一个DLL,不会造成使用者项目依赖冲突,支持 .NET 4.0 堪称屎山项目的救星。现在还有很多.NET FX4.0 的项目,这些项目因历史遗留原因或硬件限制,不能更换 .NET Core 版本。因此这些项目很难使用到现有的开源库,不能使用可靠的开源库,那么很多时候都要自行实现,在堆积代码的同时,项目也有可能越来越乱,代码越来越渣,项目逐渐变得不稳定。
快速开始
开源地址:https://github.com/2881099/FreeScheduler
dotnet add package FreeScheduler
或者
Install-Package FreeScheduler
public static Scheduler scheduler = new Scheduler(new MyTaskHandler()); //单例模式,尽量保证只创建一次
临时任务
临时任务属于内存任务,不可持久化。
void Callback()
{
Console.WriteLine("时间到了");
scheduler.AddTempTask(TimeSpan.FromSeconds(10), Callback); //设置下一次定时
}
scheduler.AddTempTask(TimeSpan.FromSeconds(10), Callback);
//如果是一次性任务,可以这样写:
scheduler.AddTempTask(TimeSpan.FromSeconds(10), () =>
{
Console.WriteLine("时间到了");
});
| Method | 说明 |
|---|---|
| string AddTempTask(TimeSpan, Action) | 创建临时的延时任务,返回 id |
| bool RemoveTempTask(string id) | 删除任务(临时任务) |
| bool ExistsTempTask(string id) | 判断任务是否存在(临时任务) |
| int QuantityTempTask | 任务数量(临时任务) |
本地环境测试 50万 个临时任务,占用内存 383M,全部执行完成耗时 70秒。
- Quartz.net 内存溢出,耗时 50秒
- FluentScheduler 占用内存 1700M,耗时 未知
- HashedWheelTimer 占用内存 213M,耗时 34秒
我尝试过把 FreeScheduler 内核改成 HashedWheelTimer 内存占用更高(600兆),原因是 FreeScheduler 功能需要占用更多资源。
循环任务
- 临时任务是一次性触发,触发体是 Action 委托
- 循环任务是周期性重复触发,触发体是 FreeScheduler.ITaskHandler,如上述 MyTestHandler
| Method | 说明 |
|---|---|
| void ctor(ITaskHandler) | 指定任务调度器(单例) |
| string AddTask(string topic, string body, int round, int seconds) | 创建循环定时任务,返回 id |
| string AddTask(string topic, string body, int[] seconds) | 创建每轮间隔不同的定时任务,返回 id |
| string AddTaskRunOnDay(..) | 创建每日循环任务,指定utc时间,返回 id |
| string AddTaskRunOnWeek(..) | 创建每周循环任务,指定utc时间,返回 id |
| string AddTaskRunOnMonth(..) | 创建每月循环任务,指定utc时间,返回 id |
| string AddTaskCustom(string topic, string body, string expression) | 创建自定义任务,返回 id |
| bool RemoveTask(string id) | 删除任务 |
| bool ExistsTask(string id) | 判断任务是否存在 |
| bool ResumeTask(string id) | 恢复已暂停的任务 |
| bool PauseTask(string id) | 暂停正在运行的任务 |
| TaskInfo[] FindTask(lambda) | 查询正在运行中的任务 |
| int QuantityTask | 任务数量 |
//每5秒触发,执行N次
var id = scheduler.AddTask("topic1", "body1", round: -1, 5);
//每次 不同的间隔秒数触发,执行6次
var id = scheduler.AddTask("topic1", "body1", new [] { 5, 5, 10, 10, 60, 60 });
//每天 20:00:00 触发,指定utc时间,执行N次
var id = scheduler.AddTaskRunOnDay("topic1", "body1", round: -1, "20:00:00");
//每周一 20:00:00 触发,指定utc时间,执行1次
var id = scheduler.AddTaskRunOnWeek("topic1", "body1", round: 1, "1:20:00:00");
//每月1日 20:00:00 触发,指定utc时间,执行12次
var id = scheduler.AddTaskRunOnMonth("topic1", "body1", round: 12, "1:20:00:00");
Cron
由于 .NET Cron 组件普遍不支持年,因此 FreeScheduler 默认没有集成,但是很容易扩展实现,如下:
var id = scheduler.AddTaskCustom("topic1", "body1", "0/1 * * * * ? ");
public static Scheduler scheduler = new Scheduler(new MyTaskHandler(), new CronCustomHandler()); //单例模式,尽量保证只创建一次
class CronCustomHandler : FreeScheduler.ITaskIntervalCustomHandler
{
public TimeSpan? NextDelay(TaskInfo task)
{
//利用 cron 功能库解析 task.IntervalArgument 得到下一次执行时间
//与当前时间相减,得到 TimeSpan,若返回 null 则任务完成
return TimeSpan.FromSeconds(5);
}
}
持久化
FreeScheduler 把任务分为两种类型,临时任务和循环任务,注意临时任务不支持持久化。
当前已支持 数据库或Redis 持久化实现,各有优缺点:
- 数据库,性能低,方便接入任务管理(后台管理系统)
- Redis,性能高,由于分页的特点,接入任务管理功能略难
使用持久化只需要把 Scheduler 构造参数修改,如下:
var fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, "data source=task.db;max pool size=5")
.UseAutoSyncStructure(true)
.UseNoneCommandParameter(true)
.UseMonitorCommand(cmd => Console.WriteLine($"=========sql: {cmd.CommandText}\r\n"))
.Build();
Scheduler scheduler = new Scheduler(new MyTaskHandler(fsql));
class MyTaskHandler : FreeScheduler.TaskHandlers.FreeSqlHandler
{
public MyTaskHandler(IFreeSql fsql) : base(fsql) { }
public override void OnExecuting(Scheduler scheduler, TaskInfo task)
{
Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss.fff")}] {task.Topic} 被执行");
}
}
Redis 持久化请安装:
dotnet add package FreeScheduler.TaskHandlers.FreeRedis
Install-Package FreeScheduler.TaskHandlers.FreeRedis
管理任务
// 使用 FreeSql 或者 SQL 查询 TaskInfo、TaskLog 两个表进行分页显示
fsql.Select<TaskInfo>().Count(out var total).Page(pageNumber, 30).ToList();
fsql.Select<TaskLog>().Count(out var total).Page(pageNumber, 30).ToList();
//暂停任务
scheduler.PauseTask(id);
//恢复暂停的任务
scheduler.ResumeTask(id);
//删除任务
scheduler.RemoveTask(id);
性能参考
| FreeScheduler | Quartz.net | FluentScheduler | HashedWheelTimer |
|---|---|---|---|
| (500,000 Tasks + 10s) | (500,000 Tasks + 10s) | (500,000 Tasks + 10s) | (500,000 Tasks + 10s) |
![]() |
![]() |
![]() |
![]() |
| 383M | 1700+M | StackOverflow | 213M |
| 70563.6066ms | 50692.5365ms | 未知 | 33697.8758ms |
FluentScheduler 单个 Registry 测试正常,但目测单线程执行(间隔1-10ms),处理速度不理想
我尝试把 FreeScheduler 内核改成 HashedWheelTimer 内存占用更高(600兆),结论:FreeScheduler 功能需要占用更多资源
结束语
.NET 定时任务组件太多了,以至于过去这些年都还没有正式推广过,希望能帮助到有需求的朋友。
开源地址:https://github.com/2881099/FreeScheduler
作者是什么人?
作者是一个入行 18年的老批,他目前写的.net 开源项目有:
| 开源项目 | 描述 | 开源地址 | 开源协议 |
|---|---|---|---|
| ImCore | 聊天系统架构 | https://github.com/2881099/im | MIT |
| FreeRedis | Redis SDK | https://github.com/2881099/FreeRedis | MIT |
| csredis | https://github.com/2881099/csredis | MIT | |
| FightLandlord | 斗DI主网络版 | https://github.com/2881099/FightLandlord | 学习用途 |
| FreeScheduler | 定时任务 | https://github.com/2881099/FreeScheduler | MIT |
| IdleBus | 空闲容器 | https://github.com/2881099/IdleBus | MIT |
| FreeSql | ORM | https://github.com/dotnetcore/FreeSql | MIT |
| FreeSql.Cloud | 分布式tcc/saga | https://github.com/2881099/FreeSql.Cloud | MIT |
| FreeSql.AdminLTE | 低代码后台生成 | https://github.com/2881099/FreeSql.AdminLTE | MIT |
| FreeSql.DynamicProxy | 动态代理 | https://github.com/2881099/FreeSql.DynamicProxy | 学习用途 |
需要的请拿走,这些都是最近几年的开源作品,以前更早写的就不发了。
[开源内卷] .NET 定时任务 -- FreeScheduler 支持 cron、持久化、可变定时设置的更多相关文章
- 开源:Taurus.MVC-Java 版本框架 (支持javax.servlet.*和jakarta.servlet.*双系列,内集成微服务客户端)
版本说明: 因为之前有了Taurus.MVC-DotNet 版本框架,因此框架标了-Java后缀. .Net 版本: 开源文章:开源:Taurus.MVC-DotNet 版本框架 (支持.NET C ...
- .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器
.NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器 北京时间今天凌晨的 Connect(); 大会上,多少程序员的假想成为现实. ...
- win10开启 linux Bash命令(win10内置了linux系统支持)
win10开启 Ubuntu linux Bash命令(win10内置了linux系统支持) 第一步: 先在设置→更新和安全→针对开发人员中选择"开发人员模式",点击后会下载&qu ...
- .NET 5 尝鲜 - 开源项目TerminalMACS WPF管理端支持.NET 5
.NET 5 尝鲜 - 开源项目TerminalMACS WPF管理端支持.NET 5 一个使用 Prism 作为模块化框架.基于多个开源控件库作为UI控件选择.集成开源 UI 界面设计的 .NET ...
- 解决Python内CvCapture视频文件格式不支持问题
解决Python内CvCapture视频文件格式不支持问题 在读取视频文件调用默认的摄像头cv.VideoCapture(0)会出现下面的视频格式问题 CvCapture_MSMF::initStre ...
- involution 内卷化
involution 内卷化 虽然不熟悉 involution,但是我想起另外两个常用词:evolution(进化)和 revolution(革命). 它们共同的词根volute,拉丁语原意是&quo ...
- 从HashMap面试聊聊互联网内卷
微信公众号:大黄奔跑 关注我,可了解更多有趣的面试相关问题. 写在之前 毫无疑问,回想2020年有什么词出现在眼前最多的,无疑是"996"和"内卷",从马老师的 ...
- UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(二)
上篇UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(一) 讲到该控件的需要和设计过程. 这篇讲讲开发过程中一些重要问题解决. 1.支持 ...
- 完整的定时任务解决方案Spring集成+定时任务本身管理+DB持久化+集群
完整的定时任务解决方案Spring集成+定时任务本身管理+DB持久化+集群 maven依赖 <dependency> <groupId>org.quartz-scheduler ...
随机推荐
- JavaScript数据类型BigInt实践之id数值太大,导致前后端交互异常
项目开发中前后端数据交互常会使用id作为主键索引,通常id数值都不大,使用number类型就可以表示处理,但对于一些分布式id或其他情况,id数值太大且超过了JS的最大处理数(Math.pow(2, ...
- php 使用phpqrcode生成二维码并上传到OSS
一般情况调用phpqrcode第三方插件 会把生成的二维码图片保存到服务器,不保存服务器也会以header头的形式输出到浏览器,(我们不允许把图片文件保存的liunx服务器,只能保存到阿里云OSS存储 ...
- VMware虚拟机基于contos 7 搭建lnmp环境全过程
这个环境也整了几次了,由于本人比较懒,没有记住.找资料很麻烦,就自己动手咯 1.下载VMware虚拟机 (有注册码) 地址:http://www.zdfans.com/5928.html ...
- 【Redis】简单动态字符串SDS
C语言字符串 char *str = "redis"; // 可以不显式的添加\0,由编译器添加 char *str = "redis\0"; // 也可以添加 ...
- 分享一个网上搜不到的「Redis」实现「聊天回合制」的方案
前言 为什么说网上搜不到,因为关于聊天回合制的方案作者本人快把百度搜秃噜了也没找到,好在最终是公司一个关系不错的大佬帮提供了点思路,最终作者将其完整实现了出来. 分享出来大家可以收藏,万一你哪天也碰到 ...
- Java注解和反射
1.注解(Annotation) 1.1.什么是注解(Annotation) 注解不是程序本身,可以在程序编译.类加载和运行时被读取,并执行相应的处理.注解的格式为"@注释名(参数值)&qu ...
- 实现领域驱动设计 - 使用ABP框架 - 通用准则
在进入细节之前,让我们看看一些总体的 DDD 原则 数据库提供者 / ORM 无关性 领域和应用程序层应该与 ORM / 数据库提供程序 无关.它们应该只依赖于 Repository 接口,而 Rep ...
- SAP 实例- 页签tabsrip
屏幕页签:项目上有一需求,对标准TCODE 一个屏幕增加一个页签.于是做了个例子. 下面屏幕有两个页签. 我们来看一下屏幕结构.100屏幕是主屏幕,101,102是子屏幕,对应页签test1,test ...
- 一文掌握GitHub Actions基本概念与配置
CI/CD包含很多流程,如拉取代码.测试.构建打包.登录远程服务器.部署发布等等. 而Github Actions是GitHub推出的一个CI/CD工具,类似工具还有TravisCI.Jenkins等 ...
- centos通过日志查入侵
1. Linux查看/var/log/wtmp文件查看可疑IP登陆 last -f /var/log/wtmp 该日志文件永久记录每个用户登录.注销及系统的启动.停机的事件.因此随着系统正常运行时间的 ...



