简介

过去,如果在业务中需要处理任务调度的时候,大家都会使用第三方的任务调度组件,而第三方组件有一套自己的规则,在微服务的中显得那么格格不入,这样就会造成代码臃肿,耦合性高,如果有分布式还需要搭建新的分布式环境,如果把任务调度做成组件服务,这个就完全满足了微服务的模块化,组件化,而下面谈的是在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脚本返回为真的验证
Skip( function(lastExecTime)) 限制任务基于一个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的后台托管服务编写任务调度并支持规则引擎自定义脚本的更多相关文章

  1. DTCMS插件的制作实例电子资源管理(二)Admin后台页面编写

    总目录 插件目录结构(一) Admin后台页面编写(二) 前台模板页编写(三) URL重写(四) 本实例旨在以一个实际的项目中的例子来介绍如何在dtcms中制作插件,本系列文章非入门教程,部分逻辑实现 ...

  2. windows后台服务程序编写

    Windows后台服务程序编写 1. 为什么要编写后台服务程序 工作中有一个程序需要写成后台服务的形式,摸索了一下,跟大家分享. 在windows操作系统中后台进程被称为 service. 服务是一种 ...

  3. 验证码在后台的编写,并实现点击验证码图片时时发生更新 C# 项目发布到IIS后不能用log4net写日志

    验证码在后台的编写,并实现点击验证码图片时时发生更新   验证码在软件中的地位越来越重要,有效防止这种问题对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试:下面就是实现验证码的基本步骤: ...

  4. 编写自己的Acunetix WVS漏洞扫描脚本详细教程

    AWVS提供了自定义的脚本编程接口,可是网上的资料很少,只有官方的几篇介绍和参考手册,最近研究了一下怎么编写AWVS的漏洞脚本来写一篇简单性的文章 本文以8.0为例,首先呢安装好Acunetix We ...

  5. 编写自己的Nmap(NSE)脚本

    编写自己的Nmap脚本 一.介绍 在上一篇文章Nmap脚本引擎原理中我们介绍了基本的NSE知识,这篇文章介绍如何基于Nmap框架编写简单的NSE脚本文件,下一篇文章,Nmap脚本文件分析(AMQP协议 ...

  6. 怎么样通过编写Python小程序来统计测试脚本的关键字

    怎么样通过编写Python小程序来统计测试脚本的关键字 通常自动化测试项目到了一定的程序,编写的测试代码自然就会很多,如果很早已经编写的测试脚本现在某些基础函数.业务函数需要修改,那么势必要找出那些引 ...

  7. iOS 后台持续定位详解(支持ISO9.0以上)

    iOS 后台持续定位详解(支持ISO9.0以上) #import <CoreLocation/CoreLocation.h>并实现CLLocationManagerDelegate 代理, ...

  8. 如何编写snort的检测规则

    如何编写snort的检测规则 2013年09月08日 ⁄ 综合 ⁄ 共 16976字 前言 snort是一个强大的轻量级的网络入侵检测系统.它具有实时数据流量分析和日志IP网络数据包的能力,能够进行协 ...

  9. SSM_CRUD新手练习(6)分页后台控制器编写

    经过测试基础环境已经搭建好了,现在我们开始编写CRUD. 我们来看一下查询的逻辑该怎么写: 1.访问index.jsp页面 2.index.jsp页面发送查询员工的请求 3.EmployeeContr ...

  10. 2015元旦第一弹——WP8.1应用程序栏(C#后台代码编写)

    //第一次写博文,以后还请各位道友互相关照哈.废话不多说,直接进入正题. 相信大家对于如何在XAML添加应用程序栏应该很清楚,不清楚的话,可以打开新建个Pviot应用 就有系统自带的菜单栏. 本文主要 ...

随机推荐

  1. 【论文翻译】KLMo: Knowledge Graph Enhanced Pretrained Language Model with Fine-Grained Relationships

    KLMo:建模细粒度关系的知识图增强预训练语言模型 (KLMo: Knowledge Graph Enhanced Pretrained Language Model with Fine-Graine ...

  2. misc办公室爱情

    ​ 隐藏文字password2 ​编辑 word改后缀zip解开后document.xml找到password1 ​编辑 True_lOve_i2_supReMe 用wbs43open+密码解密pdf ...

  3. 前端开发日常——CSS动画无限轮播

    近来没有什么值得写的东西,空闲的时候帮前端的同学做了些大屏上的展示模块,就放在这里写写吧,手把手"需求->设计-> 实现",受众偏新手向. 为了直观便于理解, 直接把结 ...

  4. JavaScript基础&实战(3)js中的流程控制语句、条件分支语句、for循环、while循环

    文章目录 1.流程控制语句 1.1 代码 1.2 测试结果 2.弹窗提示输入内容 2.1 代码 2.2 测试结果 3.条件分支语句 3.1 代码 3.2 测试结果 4.while和 do...whil ...

  5. 齐博x1标签动态调用数据

    示例代码如下: {qb:tag name="news_list_page_listdata02" type="cms" union="fid" ...

  6. Java函数式编程:一、函数式接口,lambda表达式和方法引用

    Java函数式编程 什么是函数式编程 通过整合现有代码来产生新的功能,而不是从零开始编写所有内容,由此我们会得到更加可靠的代码,并获得更高的效率 我们可以这样理解:面向对象编程抽象数据,函数式编程抽象 ...

  7. 5.@pytest.mark.parametrize()数据驱动

    简介: pytest.mark.parametrize 是 pytest 的内置装饰器,它允许你在 function 或者 class 上定义多组参数和 fixture 来实现数据驱动. @pytes ...

  8. JUC中的AQS底层详细超详解

    摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...

  9. kubelet忽然不可用

    原因,有可能机器的cpu信息有变化(扩容或者缩容)解决办法: 删掉/opt/var/lib/kubelet目录下(或者/data/lib/kubelet)cpu_manager_state文件 然后m ...

  10. 三十三、HPA实现自动扩缩容

    通过HPA实现业务应用的动态扩缩容 HPA控制器介绍 当系统资源过高的时候,我们可以使用如下命令来实现 Pod 的扩缩容功能 $ kubectl -n luffy scale deployment m ...