@[toc]
一、ScheduleMaster 核心概念
二、ScheduleMaster 应用场景
三、ScheduleMaster 项目落地
工具
- ScheduleMaster
网盘下载地址:
链接:https://pan.baidu.com/s/1LcCH...
提取码:eyup - Demo 项目
- ScheduleMaster
步骤
运行ScheduleMaster
启动命令
#进入Hos.ScheduleMaster.Web\bin\Release\netcoreapp3.1\publish目录中启动 #备注:默认数据库为sqlserver,如果其他数据库需要在appsettings.json中修改数据库类型和数据库连接地址 dotnet Hos.ScheduleMaster.Web.dll #进入Hos.ScheduleMaster.QuartzHost\bin\Release\netcoreapp3.1\publish 目录中启动 dotnet Hos.ScheduleMaster.QuartzHost.dll --urls http://*:30001
浏览器运行结果:http://localhost:30000,用户名:admin 密码:111111
如图:
Demo项目
新建一个订单回收API接口
private readonly ILogger
logger; public HomeController(ILogger _logger) { logger = _logger; } /// /// 超时订单回收接口 /// ///[HttpPost] public IActionResult OrderCancel() { logger.LogInformation("回收订单任务"); return Ok("回收订单任务"); }
新建任务
四、ScheduleMaster 运行原理
原理
- Master 概念
主节点:协调 Hos.ScheduleMaster.Web - Node 概念
工作节点:执行业务 Hos.ScheduleMaster.QuartzHost - 数据库
用来存储任务信息。 - 全局架构
如图: 执行过程
客户端---->Hos.ScheduleMaster.Web(master节点)---->Hos.ScheduleMaster.QuartzHost(工作节点)---->订单回收接口master节点的核心
- 选择工作节点
- 指定工作节点,执行任务
工作节点的核心
- 取出任务配置信息
使用Quartz根据配置运行任务
- 使用HttpClient 调用接口
使用反射调用程序集方法
五、ScheduleMaster 程序集任务
- Master 概念
工具
- 控制台Demo项目
- ScheduleMaster
步骤
新建一个Console项目
//项目安装 ScheduleMaster
新建OrderServer类继承
public class OrderServer:TaskBase { public override void Run(TaskContext context) { //超时订单逻辑 context.WriteLog("订单开始回收.......成功"); } }
ScheduleMaster 配置
六、ScheduleMaster API接口任务(使用代码自定义创建任务)
实现代码如下:
///
/// 通过代码创建任务 /// ///[HttpPost("CreateTask")] public async Task CreateTask() { HttpClient client = new HttpClient(); //登录 设置用户名和密码 client.DefaultRequestHeaders.Add("ms_auth_user", "admin"); client.DefaultRequestHeaders.Add("ms_auth_secret", MD5($"admin{MD5("111111")}admin")); List > args = new List >(); args.Add(new KeyValuePair ("MetaType", "2")); args.Add(new KeyValuePair ("RunLoop", "true")); args.Add(new KeyValuePair ("CronExpression", "0/5 * * * * ?")); args.Add(new KeyValuePair ("Remark", "By Xunit Tester Created")); args.Add(new KeyValuePair ("StartDate", DateTime.Today.ToString("yyyy-MM-dd HH:mm:ss"))); args.Add(new KeyValuePair ("Title", "order_cancel_http_api")); args.Add(new KeyValuePair ("HttpRequestUrl", "http://localhost:5000/Order")); args.Add(new KeyValuePair ("HttpMethod", "POST")); args.Add(new KeyValuePair ("HttpContentType", "application/json")); args.Add(new KeyValuePair ("HttpHeaders", "[]")); args.Add(new KeyValuePair ("HttpBody", "{ \"Posts\": [{ \"PostId\": 666, \"Title\": \"tester\", \"Content\":\"testtesttest\" }], \"BlogId\": 111, \"Url\":\"qweqrrttryrtyrtrtrt\" }")); HttpContent reqContent = new FormUrlEncodedContent(args); var response = await client.PostAsync("http://localhost:30000/api/Task/Create", reqContent); var content = await response.Content.ReadAsStringAsync(); logger.LogInformation(content); return Ok("创建成功"); } /// /// MD5加密 /// /// ///public static string MD5(string prestr) { StringBuilder sb = new StringBuilder(32); MD5 md5 = new MD5CryptoServiceProvider(); byte[] t = md5.ComputeHash(Encoding.GetEncoding("UTF-8").GetBytes(prestr)); for (int i = 0; i < t.Length; i++) { sb.Append(t[i].ToString("x").PadLeft(2, '0')); } return sb.ToString(); } 官方API
API Server 对接流程
对于开放接口来说,使用签名验证已经是必不可少的一环,这是保证系统安全性的重要手段。看一下核心对接流程:- 在控制台中创建好专用的API对接用户账号。
- 使用对接账号的用户名设置为http header中的
ms_auth_user
值。 - 使用经过哈希运算过的秘钥设置为http header中的
ms_auth_secret值
,计算规则:按{用户名}{hash(密码)}{用户名}的格式拼接得到字符串str,然后再对str做一次hash运算即得到最终秘钥,hash函数是小写的32位MD5算法。 使用form格式发起http调用,如果非法用户会返回401-Unauthorized。
代码示例:HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Add("ms_auth_user", "admin"); client.DefaultRequestHeaders.Add("ms_auth_secret", SecurityHelper.MD5($"admin{SecurityHelper.MD5("111111")}}admin"));
签名验证这块设计的比较简单,具体源码逻辑可以参考
Hos.ScheduleMaster.Web.Filters.AccessControlFilter
。
API返回格式
所有接口采用统一的返回格式,字段如下:参数名称 参数类型 说明 Success bool 是否成功 Status int 结果状态,0-请求失败 1-请求成功 2-登录失败 3-参数异常 4-数据异常 Message string 返回的消息 Data object 返回的数据 创建程序集任务
- 接口地址:
http://yourip:30000/api/task/create
- 请求类型:
POST
- 参数格式:
application/x-www-form-urlencoded
- 返回结果:创建成功返回任务id
- 参数列表:
参数名称 参数类型 是否必填 说明 MetaType int 是 任务类型,这里固定是1 Title string 是 任务名称 RunLoop bool 是 是否按周期执行 CronExpression string 否 cron表达式,如果RunLoop为true则必填 AssemblyName string 是 程序集名称 ClassName string 是 执行类名称,包含完整命名空间 StartDate DateTime 是 任务开始时间 EndDate DateTime 否 任务停止时间,为空表示不限停止时间 Remark string 否 任务描述说明 Keepers List 否 监护人id Nexts List 否 子级任务id Executors List 否 执行节点名称 RunNow bool 否 创建成功是否立即启动 Params List 否 自定义参数列表,也可以通过CustomParamsJson字段直接传json格式字符串 ScheduleParam:
参数名称 参数类型 是否必填 说明 ParamKey string 是 参数名称 ParamValue string 是 参数值 ParamRemark string 否 参数说明 代码示例:
HttpClient client = new HttpClient(); List
> args = new List >(); args.Add(new KeyValuePair ("MetaType", "1")); args.Add(new KeyValuePair ("RunLoop", "true")); args.Add(new KeyValuePair ("CronExpression", "33 0/8 * * * ?")); args.Add(new KeyValuePair ("Remark", "By Xunit Tester Created")); args.Add(new KeyValuePair ("StartDate", DateTime.Today.ToString("yyyy-MM-dd HH:mm:ss"))); args.Add(new KeyValuePair ("Title", "程序集接口测试任务")); args.Add(new KeyValuePair ("AssemblyName", "Hos.ScheduleMaster.Demo")); args.Add(new KeyValuePair ("ClassName", "Hos.ScheduleMaster.Demo.Simple")); args.Add(new KeyValuePair ("CustomParamsJson", "[{\"ParamKey\":\"k1\",\"ParamValue\":\"1111\",\"ParamRemark\":\"r1\"},{\"ParamKey\":\"k2\",\"ParamValue\":\"2222\",\"ParamRemark\":\"r2\"}]")); args.Add(new KeyValuePair ("Keepers", "1")); args.Add(new KeyValuePair ("Keepers", "2")); //args.Add(new KeyValuePair ("Nexts", "")); //args.Add(new KeyValuePair ("Executors", "")); HttpContent reqContent = new FormUrlEncodedContent(args); var response = await client.PostAsync("http://localhost:30000/api/Task/Create", reqContent); var content = await response.Content.ReadAsStringAsync(); Debug.WriteLine(content); 要提一下的是,使用API创建任务的方式不支持上传程序包,所以在任务需要启动时要确保程序包已通过其他方式上传,否则会启动失败。
- 接口地址:
创建HTTP任务
- 接口地址:
http://yourip:30000/api/task/create
- 请求类型:
POST
- 参数格式:
application/x-www-form-urlencoded
- 返回结果:创建成功返回任务id
- 参数列表:
参数名称 参数类型 是否必填 说明 MetaType int 是 任务类型,这里固定是2 Title string 是 任务名称 RunLoop bool 是 是否按周期执行 CronExpression string 否 cron表达式,如果RunLoop为true则必填 StartDate DateTime 是 任务开始时间 EndDate DateTime 否 任务停止时间,为空表示不限停止时间 Remark string 否 任务描述说明 HttpRequestUrl string 是 请求地址 HttpMethod string 是 请求方式,仅支持GET\POST\PUT\DELETE HttpContentType string 是 参数格式,仅支持application/json和application/x-www-form-urlencoded HttpHeaders string 否 自定义请求头,ScheduleParam列表的json字符串 HttpBody string 是 如果是json格式参数,则是对应参数的json字符串;如果是form格式参数,则是对应ScheduleParam列表的json字符串。 Keepers List 否 监护人id Nexts List 否 子级任务id Executors List 否 执行节点名称 RunNow bool 否 创建成功是否立即启动 代码示例:
HttpClient client = new HttpClient(); List
> args = new List >(); args.Add(new KeyValuePair ("MetaType", "2")); args.Add(new KeyValuePair ("RunLoop", "true")); args.Add(new KeyValuePair ("CronExpression", "22 0/8 * * * ?")); args.Add(new KeyValuePair ("Remark", "By Xunit Tester Created")); args.Add(new KeyValuePair ("StartDate", DateTime.Today.ToString("yyyy-MM-dd HH:mm:ss"))); args.Add(new KeyValuePair ("Title", "Http接口测试任务")); args.Add(new KeyValuePair ("HttpRequestUrl", "http://localhost:56655/api/1.0/value/jsonpost")); args.Add(new KeyValuePair ("HttpMethod", "POST")); args.Add(new KeyValuePair ("HttpContentType", "application/json")); args.Add(new KeyValuePair ("HttpHeaders", "[]")); args.Add(new KeyValuePair ("HttpBody", "{ \"Posts\": [{ \"PostId\": 666, \"Title\": \"tester\", \"Content\":\"testtesttest\" }], \"BlogId\": 111, \"Url\":\"qweqrrttryrtyrtrtrt\" }")); HttpContent reqContent = new FormUrlEncodedContent(args); var response = await client.PostAsync("http://localhost:30000/api/Task/Create", reqContent); var content = await response.Content.ReadAsStringAsync(); Debug.WriteLine(content); - 接口地址:
创建延时任务
- 接口地址:
http://yourip:30000/api/delaytask/create
- 请求类型:
POST
- 参数格式:
application/x-www-form-urlencoded
- 返回结果:创建成功返回任务id
- 参数列表:
参数名称 参数类型 是否必填 说明 SourceApp string 是 来源 Topic string 是 主题 ContentKey string 是 业务关键字 DelayTimeSpan int 是 延迟相对时间 DelayAbsoluteTime DateTime 是 延迟绝对时间 NotifyUrl string 是 回调地址 NotifyDataType string 是 回调参数格式,仅支持application/json和application/x-www-form-urlencoded NotifyBody string 是 回调参数,json格式字符串 代码示例:
for (int i = 0; i < 5; i++) { int rndNum = new Random().Next(20, 500); List
> args = new List >(); args.Add(new KeyValuePair ("SourceApp", "TestApp")); args.Add(new KeyValuePair ("Topic", "TestApp.Trade.TimeoutCancel")); args.Add(new KeyValuePair ("ContentKey", i.ToString())); args.Add(new KeyValuePair ("DelayTimeSpan", rndNum.ToString())); args.Add(new KeyValuePair ("DelayAbsoluteTime", DateTime.Now.AddSeconds(rndNum).ToString("yyyy-MM-dd HH:mm:ss"))); args.Add(new KeyValuePair ("NotifyUrl", "http://localhost:56655/api/1.0/value/delaypost")); args.Add(new KeyValuePair ("NotifyDataType", "application/json")); args.Add(new KeyValuePair ("NotifyBody", "{ \"Posts\": [{ \"PostId\": 666, \"Title\": \"tester\", \"Content\":\"testtesttest\" }], \"BlogId\": 111, \"Url\":\"qweqrrttryrtyrtrtrt\" }")); HttpContent reqContent = new FormUrlEncodedContent(args); var response = await client.PostAsync("http://localhost:30000/api/DelayTask/Create", reqContent); var content = await response.Content.ReadAsStringAsync(); Debug.WriteLine(content); } 七、ScheduleMaster 集群和集群原理
- 接口地址:
主要针对工作节点使用集群
步骤
进入工作节点项目根目录下【ScheduleMasterCore\Hos.ScheduleMaster.QuartzHost\bin\Release\netcoreapp3.1\publish】,更改配置文件【appsettings.json】, 因为要启动多个节点,只需要更改节点名称和端口号即可【切记:节点名称不能够重复】,如下:
"NodeSetting": { "IdentityName": "worker1", //节点名称 "Role": "worker", "Protocol": "http", "IP": "localhost", "Port": 30001, //端口号 "Priority": 1, "MaxConcurrency": 20 }
启动
#进入Hos.ScheduleMaster.QuartzHost\bin\Release\netcoreapp3.1\publish 目录中启动 dotnet Hos.ScheduleMaster.QuartzHost.dll --urls http://*:30002
- 运行结果,如图:
场景