ScheduleMaster分布式任务调度中心基本使用和原理

@[toc]

一、ScheduleMaster 核心概念

  • 概念
    统一执多个系统的任务【回收超时订单,清理垃圾信息 】,如图:
    ScheduleMaster分布式任务调度中心基本使用和原理_第1张图片

二、ScheduleMaster 应用场景

  • 场景
    主要应用在微服务系统中。
    如图:
    ScheduleMaster分布式任务调度中心基本使用和原理_第2张图片

三、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

        运行结果如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第3张图片
        ScheduleMaster分布式任务调度中心基本使用和原理_第4张图片

        浏览器运行结果:http://localhost:30000,用户名:admin 密码:111111
        如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第5张图片
        ScheduleMaster分布式任务调度中心基本使用和原理_第6张图片

    • Demo项目

      • 新建一个订单回收API接口

          private readonly ILogger logger;
          public HomeController(ILogger _logger) {
              logger = _logger;
          } 
          /// 
          /// 超时订单回收接口
          /// 
          /// 
          [HttpPost]
          public IActionResult OrderCancel() 
          {
              logger.LogInformation("回收订单任务");
              return Ok("回收订单任务");
          }
    • 新建任务

      • 点击任务列表
        如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第7张图片
      • 点击创建任务按钮->选择 “基础信息配置”
        如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第8张图片
        ScheduleMaster分布式任务调度中心基本使用和原理_第9张图片
        ScheduleMaster分布式任务调度中心基本使用和原理_第10张图片

        选择“元数据配置”,在点击“保存”即可,如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第11张图片

      • 运行结果(每隔5秒进行调用订单回收接口),如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第12张图片

四、ScheduleMaster 运行原理

  • 原理

    • Master 概念
      主节点:协调 Hos.ScheduleMaster.Web
    • Node 概念
      工作节点:执行业务 Hos.ScheduleMaster.QuartzHost
    • 数据库
      用来存储任务信息。
    • 全局架构
      如图:
      ScheduleMaster分布式任务调度中心基本使用和原理_第13张图片
    • 执行过程
      客户端---->Hos.ScheduleMaster.Web(master节点)---->Hos.ScheduleMaster.QuartzHost(工作节点)---->订单回收接口

      • master节点的核心

        • 选择工作节点
        • 指定工作节点,执行任务
      • 工作节点的核心

        • 取出任务配置信息
        • 使用Quartz根据配置运行任务

          • 使用HttpClient 调用接口
          • 使用反射调用程序集方法

            五、ScheduleMaster 程序集任务

  • 工具

    • 控制台Demo项目
    • ScheduleMaster
  • 步骤

    • 新建一个Console项目

        //项目安装  
        ScheduleMaster

      新建OrderServer类继承

         public  class OrderServer:TaskBase
         {  
           public override void Run(TaskContext context)
           {
              //超时订单逻辑
              context.WriteLog("订单开始回收.......成功");
           }
         }
      • 控制台项目打包
        项目编译后,进入 bin文件件,将Hos.ScheduleMaster.Base.dll除外的所有的文件打包成压缩文件,如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第14张图片
    • ScheduleMaster 配置

      • 点击“任务列表”目录,点击“创建任务”按钮,任务类型选择“程序集任务”,如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第15张图片ScheduleMaster分布式任务调度中心基本使用和原理_第16张图片ScheduleMaster分布式任务调度中心基本使用和原理_第17张图片在这里插入图片描述ScheduleMaster分布式任务调度中心基本使用和原理_第18张图片

六、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
      • 运行结果,如图:
        ScheduleMaster分布式任务调度中心基本使用和原理_第19张图片
  • 场景

    • 如果当前的工作节点宕机了,会不会转移到其他的工作节点上?
      需要更改任务的执行节点,选择多个执行节点,如图:
      ScheduleMaster分布式任务调度中心基本使用和原理_第20张图片

      有两个工作节点,30001和30002,其中把30001宕机了[有个心跳检测的API接口,定时任务不断的检测当前的节点是否可用],会直接转移到其他节点执行任务,运行结果如下:
      ScheduleMaster分布式任务调度中心基本使用和原理_第21张图片

你可能感兴趣的:(分布式任务调度)