通过surging的后台托管服务编写任务调度并支持规则引擎自定义脚本
简介
过去,如果在业务中需要处理任务调度的时候,大家都会使用第三方的任务调度组件,而第三方组件有一套自己的规则,在微服务的中显得那么格格不入,这样就会造成代码臃肿,耦合性高,如果有分布式还需要搭建新的分布式环境,如果把任务调度做成组件服务,这个就完全满足了微服务的模块化,组件化,而下面谈的是在surging 中如何支持规则引擎自定义脚本。
调度频率设置
首先在开始之前,先看看如何通过脚本分配多种调度计划,先看下表:
| 方法 | 描述 |
|---|---|
| EveryMinute() | 每分钟执行一次任务 |
| EveryFiveMinutes(); | 每五分钟执行一次任务 |
| EveryTenMinutes(); | 每十分钟执行一次任务 |
| EveryThirtyMinutes() | 每半小时执行一次任务 |
| Hourly(); | 每小时执行一次任务 |
HourlyAt(10) |
每一个小时的第 10 分钟运行一次 |
Daily() |
每到午夜执行一次任务 |
| DailyAt("3:00") | 在 3:00 执行一次任务 |
| TwiceDaily(1, 3) | 在 1:00 和 3:00 分别执行一次任务 |
Weekly() |
每周执行一次任务 |
Monthly() |
每月执行一次任务 |
| MonthlyOn(4, "3:00") | 在每个月的第四天的 3:00 执行一次任务 |
Quarterly() |
每季度执行一次任务 |
Yearly() |
每年执行一次任务 |
Timezone("utc") |
设置utc时区 |
举个例子,在工作日每三秒在时间8:00-23:30内执行任务。脚本如下:
parser.TimeZone(""utc"")
.Weekdays()
.SecondAt(3)
.Between(""8:00"", ""23:30"")
额外的限制条件列表如下:
| 方法 | 描述 |
|---|---|
Weekdays() |
限制任务在工作日 |
Sundays() |
限制任务在星期日 |
Mondays() |
限制任务在星期一 |
Tuesdays() |
限制任务在星期二 |
Wednesdays() |
限制任务在星期三 |
Thursdays() |
限制任务在星期四 |
Fridays() |
限制任务在星期五 |
Saturdays() |
限制任务在星期六 |
When( function(lastExecTime)) |
限制任务基于一个script脚本返回为真的验证 |
|
限制任务基于一个script脚本返回为假的验证 |
举个例子,在工作日每三秒在时间8:00-23:30内执行任务。如果设置When返回为true,skip返回false 就会执行,脚本如下:
parser.TimeZone(""utc"")
.When(function(lastExecTime){
return true;
})
.Skip(
function(lastExecTime){
return false;
})
.Weekdays()
.SecondAt(3)
.Between(""8:00"", ""23:30"")
然后在function 脚本中支持DateUtils对象,可以针对lastExecTime进行逻辑判断,比如是否是周末:DateUtils.IsWeekend(lastExecTime), 是否是今天DateUtils.IsToday(lastExecTime),代码如下:
parser.TimeZone(""utc"")
.When(function(lastExecTime){
return DateUtils.IsToday(lastExecTime);
})
.Skip(
function(lastExecTime){
return DateUtils.IsWeekend(lastExecTime);
})
.Weekdays()
.SecondAt(3)
.Between(""8:00"", ""23:30"")
编写调度服务
surging微服务引擎是支持后台管理托管服务的,如果要基于BackgroundService编写任务调度,那服务就要继承BackgroundServiceBehavior,还要继承ISingleInstance以设置注入单例模式,
首先,创建接口服务,这样就可以远程添加任务,开启关闭服务了,代码如下:
[ServiceBundle("Background/{Service}")]
public interface IWorkService : IServiceKey
{
Task<bool> AddWork(Message message);
Task StartAsync();
Task StopAsync();
}
然后创建业务领域服务,以下代码是通过规则引擎自定义脚本设置执行频率,并且可以设置execsize 以标识同时执行任务的大小,通过以下业务逻辑代码大家可以扩展支持持久化。
public class WorkService : BackgroundServiceBehavior, IWorkService, ISingleInstance
{
private readonly ILogger<WorkService> _logger;
private readonly Queue<Tuple<Message, RulesEngine.RulesEngine, SchedulerRuleWorkflow>> _queue = new Queue<Tuple<Message, RulesEngine.RulesEngine, SchedulerRuleWorkflow>>();
private readonly ConcurrentDictionary<string, DateTime> _keyValuePairs = new ConcurrentDictionary<string, DateTime>();
private readonly IServiceProxyProvider _serviceProxyProvider;
private AtomicLong _atomic=new AtomicLong(1);
private const int EXECSIZE = 1;
private CancellationToken _token; public WorkService(ILogger<WorkService> logger, IServiceProxyProvider serviceProxyProvider)
{
_logger = logger;
_serviceProxyProvider = serviceProxyProvider;
/* var script = @"parser
.Weekdays().SecondAt(3).Between(""8:00"", ""22:00"")";*/
var script = @"parser
.TimeZone(""utc"")
.When(
function(lastExecTime){
return DateUtils.IsToday(lastExecTime);
}).Skip(
function(lastExecTime){
return DateUtils.IsWeekend(lastExecTime);
}).Weekdays().SecondAt(3).Between(""8:00"", ""23:30"")";
var ruleWorkflow = GetSchedulerRuleWorkflow(script);
var messageId = Guid.NewGuid().ToString();
_keyValuePairs.AddOrUpdate(messageId, DateTime.Now, (key, value) => DateTime.Now);
_queue.Enqueue(new Tuple<Message, RulesEngine.RulesEngine, SchedulerRuleWorkflow>(new Message() { MessageId= messageId,Config=new SchedulerConfig() { IsPersistence=true} }, GetRuleEngine(ruleWorkflow), ruleWorkflow)); } public Task<bool> AddWork(Message message)
{
var ruleWorkflow = GetSchedulerRuleWorkflow(message.Config.Script);
_keyValuePairs.AddOrUpdate(message.MessageId, DateTime.Now, (key, value) => DateTime.Now);
_queue.Enqueue(new Tuple<Message, RulesEngine.RulesEngine, SchedulerRuleWorkflow>(message, GetRuleEngine(ruleWorkflow), ruleWorkflow));
return Task.FromResult(true);
} protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
_token = stoppingToken;
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
_queue.TryDequeue(out Tuple<Message, RulesEngine.RulesEngine, SchedulerRuleWorkflow>? message);
if (message != null)
{
var parser = await GetParser(message.Item3, message.Item2);
await PayloadSubscribe(parser, message.Item1, message.Item2, message.Item3);
_keyValuePairs.TryGetValue(message.Item1.MessageId, out DateTime dateTime);
parser.Build(dateTime == DateTime.MinValue ? DateTime.Now : dateTime);
}
if (!_token.IsCancellationRequested && (message == null || _atomic.GetAndAdd(1) == EXECSIZE))
{
_atomic = new AtomicLong(1);
await Task.Delay(1000, stoppingToken); }
}
catch (Exception ex){
_logger.LogError("WorkService execute error, message:{message} ,trace info:{trace} ", ex.Message, ex.StackTrace);
}
} public async Task StartAsync()
{
if (_token.IsCancellationRequested)
{
await base.StartAsync(_token);
}
} public async Task StopAsync()
{
if (!_token.IsCancellationRequested)
{
await base.StopAsync(_token);
}
} private async Task PayloadSubscribe(RulePipePayloadParser parser, Message message, RulesEngine.RulesEngine rulesEngine, SchedulerRuleWorkflow ruleWorkflow)
{
parser.HandlePayload().Subscribe(async (temperature) =>
{
try
{
if (temperature)
{
await ExecuteByPlanAsyn(message);
_logger.LogInformation("Worker exec at: {time}", DateTimeOffset.Now); }
}
catch (Exception ex) { }
finally
{
if (message.Config.IsPersistence || (!temperature && !message.Config.IsPersistence))
_queue.Enqueue(new Tuple<Message, RulesEngine.RulesEngine, SchedulerRuleWorkflow>(message, rulesEngine, ruleWorkflow)); }
});
} private async Task<bool> ExecuteByPlanAsyn(Message message)
{
var result = false;
var isExec = true;
try
{
if (!string.IsNullOrEmpty(message.RoutePath))
{
var serviceResult = await _serviceProxyProvider.Invoke<object>(message.Parameters, message.RoutePath, message.ServiceKey);
bool.TryParse(serviceResult?.ToString(), out result);
isExec = true;
}
}
catch { }
finally
{
if (isExec && message.Config.IsPersistence)
_keyValuePairs.AddOrUpdate(message.MessageId, DateTime.Now, (key, value) => DateTime.Now);
else if (!message.Config.IsPersistence)
_keyValuePairs.TryRemove(message.MessageId, out DateTime dateTime);
}
return result;
} private async Task<RulePipePayloadParser> GetParser(SchedulerRuleWorkflow ruleWorkflow, RulesEngine.RulesEngine engine)
{
var payloadParser = new RulePipePayloadParser();
var ruleResult = await engine.ExecuteActionWorkflowAsync(ruleWorkflow.WorkflowName, ruleWorkflow.RuleName, new RuleParameter[] { new RuleParameter("parser", payloadParser) });
if (ruleResult.Exception != null && _logger.IsEnabled(LogLevel.Error))
_logger.LogError(ruleResult.Exception, ruleResult.Exception.Message);
return payloadParser;
} private RulesEngine.RulesEngine GetRuleEngine(SchedulerRuleWorkflow ruleWorkFlow)
{
var reSettingsWithCustomTypes = new ReSettings { CustomTypes = new Type[] { typeof(RulePipePayloadParser) } };
var result = new RulesEngine.RulesEngine(new Workflow[] { ruleWorkFlow.GetWorkflow() }, null, reSettingsWithCustomTypes);
return result;
} private SchedulerRuleWorkflow GetSchedulerRuleWorkflow(string script)
{
var result = new SchedulerRuleWorkflow("1==1");
if (!string.IsNullOrEmpty(script))
{
result = new SchedulerRuleWorkflow(script);
}
return result;
}
}
总结
因为工作繁忙,微服务平台暂时搁置,等公司基于surging 的物联网项目上线后,再投入时间研发,surging 一直开发中未曾放弃,也许你没看到的版本才是最强大的。之前的QQ群被封了,如果感兴趣可以加:744677125
开源地址:https://github.com/fanliang11/surging
通过surging的后台托管服务编写任务调度并支持规则引擎自定义脚本的更多相关文章
- DTCMS插件的制作实例电子资源管理(二)Admin后台页面编写
总目录 插件目录结构(一) Admin后台页面编写(二) 前台模板页编写(三) URL重写(四) 本实例旨在以一个实际的项目中的例子来介绍如何在dtcms中制作插件,本系列文章非入门教程,部分逻辑实现 ...
- windows后台服务程序编写
Windows后台服务程序编写 1. 为什么要编写后台服务程序 工作中有一个程序需要写成后台服务的形式,摸索了一下,跟大家分享. 在windows操作系统中后台进程被称为 service. 服务是一种 ...
- 验证码在后台的编写,并实现点击验证码图片时时发生更新 C# 项目发布到IIS后不能用log4net写日志
验证码在后台的编写,并实现点击验证码图片时时发生更新 验证码在软件中的地位越来越重要,有效防止这种问题对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试:下面就是实现验证码的基本步骤: ...
- 编写自己的Acunetix WVS漏洞扫描脚本详细教程
AWVS提供了自定义的脚本编程接口,可是网上的资料很少,只有官方的几篇介绍和参考手册,最近研究了一下怎么编写AWVS的漏洞脚本来写一篇简单性的文章 本文以8.0为例,首先呢安装好Acunetix We ...
- 编写自己的Nmap(NSE)脚本
编写自己的Nmap脚本 一.介绍 在上一篇文章Nmap脚本引擎原理中我们介绍了基本的NSE知识,这篇文章介绍如何基于Nmap框架编写简单的NSE脚本文件,下一篇文章,Nmap脚本文件分析(AMQP协议 ...
- 怎么样通过编写Python小程序来统计测试脚本的关键字
怎么样通过编写Python小程序来统计测试脚本的关键字 通常自动化测试项目到了一定的程序,编写的测试代码自然就会很多,如果很早已经编写的测试脚本现在某些基础函数.业务函数需要修改,那么势必要找出那些引 ...
- iOS 后台持续定位详解(支持ISO9.0以上)
iOS 后台持续定位详解(支持ISO9.0以上) #import <CoreLocation/CoreLocation.h>并实现CLLocationManagerDelegate 代理, ...
- 如何编写snort的检测规则
如何编写snort的检测规则 2013年09月08日 ⁄ 综合 ⁄ 共 16976字 前言 snort是一个强大的轻量级的网络入侵检测系统.它具有实时数据流量分析和日志IP网络数据包的能力,能够进行协 ...
- SSM_CRUD新手练习(6)分页后台控制器编写
经过测试基础环境已经搭建好了,现在我们开始编写CRUD. 我们来看一下查询的逻辑该怎么写: 1.访问index.jsp页面 2.index.jsp页面发送查询员工的请求 3.EmployeeContr ...
- 2015元旦第一弹——WP8.1应用程序栏(C#后台代码编写)
//第一次写博文,以后还请各位道友互相关照哈.废话不多说,直接进入正题. 相信大家对于如何在XAML添加应用程序栏应该很清楚,不清楚的话,可以打开新建个Pviot应用 就有系统自带的菜单栏. 本文主要 ...
随机推荐
- 打造企业自己代码规范IDEA插件(中)
一些基本概念 在开始独立研发公司自己的代码规范检查规则之前,先介绍一些相关的基本概念.阿里巴巴代码规范很多规则其实都是基于开源框架PMD进行的研发.PMD用官方的话语介绍来说:PMD是一个源代码分析器 ...
- 生产管理ERP哪一款比较好?
生产管理用的是MES,企业管理用的才是ERP,这个得弄清楚!如果要谈生产管理,每家工厂的区别.差异性更大,在工厂甲用得很好的管理系统搬到工厂乙,大概率水土不服,不是软件本身的问题,而是生产的产品.部件 ...
- Apache Dolphin Scheduler 3.0.1 发布,对核心及UI相关进行优化
点亮 ️ Star · 照亮开源之路 GitHub:https://github.com/apache/dolphinscheduler 版本发布 感谢本次的 Release Manager -- ...
- AT&T汇编语法与x86语法基本区别
AT&T汇编和8086汇编语言虽然两者很相似,但是还是不能根据8086的语法规则来读AT&T汇编的吧,所以还是要看看AT&T汇编的语法规则,因为在读内核代码时,跟硬件打交道的部 ...
- Java学习之路:运算符
2022-10-10 10:34:08 1 运算符 算术运算符:+, -, *, /, %, ++, -- 赋值运算符:= 关系运算符:>, <, >=, <=, ==, != ...
- 2.ElasticSearch系列之集群权限认证
1. 在master节点上创建秘钥库 export ES_PATH_CONF="/home/elasticsearch/config" && /usr/local/ ...
- SDOI2017树点染色
题目链接 发现1操作很像lct中的access,然后它每次染的又是一个新颜色,因此同一个颜色就在同一颗splay里了,且一个点到根的权值val[i]也就是到根路径上虚边的个数,然后看access时会对 ...
- How to get the return value of the setTimeout inner function in js All In One
How to get the return value of the setTimeout inner function in js All In One 在 js 中如何获取 setTimeout ...
- 谣言检测()《Rumor Detection with Self-supervised Learning on Texts and Social Graph》
论文信息 论文标题:Rumor Detection with Self-supervised Learning on Texts and Social Graph论文作者:Yuan Gao, Xian ...
- 论文笔记 - Fantastically Ordered Prompts and Where to Find Them: Overcoming Few-Shot Prompt Order Sensitivity
prompt 的影响因素 Motivation Prompt 中 Example 的排列顺序对模型性能有较大影响(即使已经校准参见好的情况下,选取不同的排列顺序依然会有很大的方差): 校准可以大幅度提 ...