山寨版Quartz.Net任务统一调度框架

 

TaskScheduler

在日常工作中,大家都会经常遇到Win服务,在我工作的这些年中一直在使用Quartz.Net这个任务统一调度框架,也非常好用,配置简单,但是如果多个项目组的多个服务部署到一台服务器时还是不尽如人意。

这段时间很忙,也一直未更新博客了,赶上今天下班早,就研究了一下,弄了个简单版基于Timer的山寨Quartz,当然了只是实现任务调度,闲话少说直接入主题吧

一、技术准备

其实都是普通的微软技术,一想到这方我们第一想到的可能就是反射,本文用了MEF

二、框架搭建

第一我们建立项目TianWei.TaskScheduler

第二我们会想到给Timer加个参数,这里建了一个 TWTimer来继承Timer,在里面有一个属性为JobDetail(Job详情实本),这样每个TImer我们就可以把任务详情做为参数传入

  1. /// <summary>
  2. /// 自定义Timer
  3. /// </summary>
  4. public class TWTimer : System.Timers.Timer
  5. {
  6. public JobDetail JobDetail { get; set; }
  7. }

第三建立JobDetail

  1. /// <summary>
  2. /// 作业请情
  3. /// </summary>
  4. [XmlRootAttribute("xml", IsNullable = false)]
  5. public class JobDetail
  6. {
  7. /// <summary>
  8. /// 作业名称
  9. /// </summary>
  10. public string Name { get; set; }
  11.  
  12. /// <summary>
  13. /// 作业执行类
  14. /// </summary>
  15. public string JobType { get; set; }
  16.  
  17. /// <summary>
  18. /// 自定义Cron表达式
  19. /// </summary>
  20. public string CronExpression { get; set; }
  21.  
  22. /// <summary>
  23. /// 作业类型
  24. /// </summary>
  25. [XmlIgnoreAttribute]
  26. public WorkType WorkType { get; set; }
  27.  
  28. /// <summary>
  29. /// 如果是每周 周几
  30. /// </summary>
  31. [XmlIgnoreAttribute]
  32. public DayOfWeek Week { get; set; }
  33.  
  34. /// <summary>
  35. /// 执行表达式
  36. /// </summary>
  37. [XmlIgnoreAttribute]
  38. public string ExecuteExpression { get; set; }
  39.  
  40. /// <summary>
  41. /// 执行间隔 循环执行有效
  42. /// </summary>
  43. [XmlIgnoreAttribute]
  44. public int Interval { get; set; }
  45.  
  46. /// <summary>
  47. /// 作业状态 停启用
  48. /// </summary>
  49. public bool Enabled { get; set; }
  50.  
  51. /// <summary>
  52. /// 作业开始工作时间- 可为空
  53. /// </summary>
  54. public string StartTime { get; set; }
  55.  
  56. /// <summary>
  57. /// 作业结束时间-可为空
  58. /// </summary>
  59. public string EndTime { get; set; }
  60.  
  61. /// <summary>
  62. /// 作业开始工作时间-默认为最小时间
  63. /// </summary>
  64. [XmlIgnoreAttribute]
  65. public DateTime JobStartTime
  66. {
  67. get
  68. {
  69. DateTime value = DateTime.MinValue;
  70. if (StartTime != null)
  71. {
  72. DateTime.TryParse(StartTime, out value);
  73. }
  74. return value;
  75. }
  76. set { }
  77. }
  78.  
  79. /// <summary>
  80. /// 作业结束工作时间-默认为最大时间
  81. /// </summary>
  82. [XmlIgnoreAttribute]
  83. public DateTime JobEndTime
  84. {
  85. get
  86. {
  87. DateTime value = DateTime.MaxValue;
  88. if (EndTime != null)
  89. {
  90. DateTime.TryParse(EndTime, out value);
  91. }
  92. return value;
  93. }
  94. set { }
  95. }
  96. }

第四建立Job作为根据参数判断执行哪个Job

  1. public class Job
  2. {
  3. public void Execute(JobDetail jobDetail, IJob job)
  4. {
  5. if (!jobDetail.Enabled) return;
  6. if (DateTime.Now < jobDetail.JobStartTime || DateTime.Now > jobDetail.JobEndTime) return;
  7. if (jobDetail.WorkType == WorkType.Week)
  8. {
  9. if (jobDetail.Week == DateTime.Now.DayOfWeek && jobDetail.ExecuteExpression == DateTime.Now.ToString("HHmmss"))
  10. {
  11. job.Execute();
  12. }
  13. }
  14. else if (jobDetail.WorkType == WorkType.Yearly)
  15. {
  16. if (jobDetail.ExecuteExpression == DateTime.Now.ToString("MMddHHmmss"))
  17. {
  18. job.Execute();
  19. }
  20. }
  21. else if (jobDetail.WorkType == WorkType.Monthly)
  22. {
  23. if (jobDetail.ExecuteExpression == DateTime.Now.ToString("ddHHmmss"))
  24. {
  25. job.Execute();
  26. }
  27. }
  28. else if (jobDetail.WorkType == WorkType.Daily)
  29. {
  30. if (jobDetail.ExecuteExpression == DateTime.Now.ToString("HHmmss"))
  31. {
  32. job.Execute();
  33. }
  34. }
  35. else if (jobDetail.WorkType == WorkType.Loop)
  36. {
  37. job.Execute();
  38. }
  39. }
  40.  
  41. }

第五建立接口IJob,所有Job都要继承并实现Execute

  1. /// <summary>
  2. /// 作业接口
  3. /// </summary>
  4. public interface IJob
  5. {
  6. /// <summary>
  7. /// 作业需要继承的接口
  8. /// </summary>
  9. void Execute();
  10.  
  11. }

第六建立核心部分调度器,这里用到了MEF的导入和导出

  1. public class Scheduler
  2. {
  3. [ImportMany(typeof(IJob))]
  4. public List<IJob> jobs;
  5. public Dictionary<string, IJob> dicJobs;
  6. public Dictionary<string, TWTimer> dicTimer;
  7. private void Run()
  8. {
  9. var catalog = new AggregateCatalog();
  10. catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
  11. catalog.Catalogs.Add(new DirectoryCatalog(Environment.CurrentDirectory));
  12. var container = new CompositionContainer(catalog);
  13. container.ComposeParts(this);
  14. }
  15.  
  16. public void Execute()
  17. {
  18. Run();
  19. SetDicJobs();
  20. SetDicTimers();
  21. FileWatcher();
  22. }
  23.  
  24. private void SetDicJobs()
  25. {
  26. if (jobs != null)
  27. {
  28. dicJobs = new Dictionary<string, IJob>();
  29. foreach (var job in jobs)
  30. {
  31. dicJobs.Add(job.ToString(), job);
  32. }
  33. }
  34. }
  35.  
  36. private void SetDicTimers()
  37. {
  38. dicTimer = new Dictionary<string, TWTimer>();
  39. var jobList = (List<JobDetail>)XmlHelper.XmlDeserialize(typeof(List<JobDetail>), Config.ConfigPath);
  40. if (jobList != null)
  41. {
  42. foreach (var item in jobList)
  43. {
  44. SetTimer(item);
  45. }
  46. }
  47. }
  48.  
  49. /// <summary>
  50. /// Timer
  51. /// </summary>
  52. /// <param name="jobDetail"></param>
  53. private void SetTimer(JobDetail jobDetail)
  54. {
  55. TWTimer timer = new TWTimer();
  56. timer.JobDetail = CronHelper.SetCron(jobDetail);
  57. if (timer.JobDetail.WorkType == WorkType.Loop)
  58. {
  59. timer.Interval = timer.JobDetail.Interval;
  60. }
  61. else
  62. {
  63. timer.Interval = 1000;
  64. }
  65. timer.AutoReset = true;
  66. timer.Enabled = true;
  67. timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
  68. dicTimer.Add(timer.JobDetail.Name, timer);
  69. }
  70.  
  71. /// <summary>
  72. /// Timer事件
  73. /// </summary>
  74. /// <param name="source"></param>
  75. /// <param name="e"></param>
  76. private void OnTimedEvent(object source, ElapsedEventArgs e)
  77. {
  78. try
  79. {
  80. var timer = (TWTimer)source;
  81. if (dicJobs.Any(o => o.Key == timer.JobDetail.JobType))
  82. {
  83. Job job = new Job();
  84. job.Execute(timer.JobDetail, dicJobs[timer.JobDetail.JobType]);
  85. }
  86. }
  87. catch (Exception ex)
  88. {
  89. //记录日志
  90. }
  91. }
  92.  
  93. /// <summary>
  94. /// 文件监听
  95. /// </summary>
  96. private void FileWatcher()
  97. {
  98. FileSystemWatcher watcher = new FileSystemWatcher();
  99. watcher.Path = Environment.CurrentDirectory;
  100. watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  101. watcher.Filter = Config.ConfigFile;
  102. watcher.Changed += new FileSystemEventHandler(OnChanged);
  103. watcher.EnableRaisingEvents = true;
  104. }
  105.  
  106. /// <summary>
  107. /// 文件改动事件
  108. /// </summary>
  109. /// <param name="source"></param>
  110. /// <param name="e"></param>
  111. private void OnChanged(object source, FileSystemEventArgs e)
  112. {
  113. var jobList = (List<JobDetail>)XmlHelper.XmlDeserialize(typeof(List<JobDetail>), Config.ConfigPath);
  114. if (jobList != null)
  115. {
  116. foreach (var item in jobList)
  117. {
  118. if (dicTimer.Any(o => o.Key == item.Name))
  119. {
  120. var timer = dicTimer[item.Name];
  121. if (item.JobType != timer.JobDetail.JobType || item.CronExpression != timer.JobDetail.CronExpression)
  122. {
  123. timer.JobDetail = CronHelper.SetCron(item);
  124. if (timer.JobDetail.WorkType == WorkType.Loop)
  125. {
  126. timer.Interval = timer.JobDetail.Interval;
  127. }
  128. else
  129. {
  130. timer.Interval = 1000;
  131. }
  132. }
  133. timer.JobDetail.Enabled = item.Enabled;
  134. timer.JobDetail.StartTime = item.StartTime;
  135. timer.JobDetail.EndTime = item.EndTime;
  136. }
  137. else
  138. {
  139. SetTimer(item);
  140. }
  141. }
  142. }
  143. }
  144. }

其它辅助类详见源码

三、使用方法

到这里一个任务调度框架的核心就完成了,下面我信介绍怎么使用

第一在我们想要用到的项目要填加引用TianWei.TaskScheduler

第二在想做为任务的类继承IJob并实现Execute方法并在类上面加上[Export(typeof(IJob))]

第三在服务程序或控制台程序中引用相关类(这里以控制台程序测试)

第四增加配置文件在App.config中增加<add key="JobsConfig" value="\Jobs.config"/>  在Jobs.config中增加如下配置一个任务一个JobDetail

  1. <?xml version="1.0"?>
  2. <ArrayOfJobDetail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  3. <JobDetail>
  4. <Name>Job1</Name>
  5. <JobType>TianWei.TaskScheduler.Jobs.Job1</JobType>
  6. <CronExpression>1 00 00 * * *</CronExpression>
  7. <Enabled>true</Enabled>
  8. <StartTime>2015-05-1 12:00:00</StartTime>
  9. <EndTime>2015-05-30 12:00:00</EndTime>
  10. </JobDetail>
  11. <JobDetail>
  12. <Name>Job2</Name>
  13. <JobType>TianWei.TaskScheduler.Jobs.Job2</JobType>
  14. <CronExpression>05 37 14 0 * *</CronExpression>
  15. <Enabled>true</Enabled>
  16. <StartTime></StartTime>
  17. <EndTime>2015-05-30 12:00:00</EndTime>
  18. </JobDetail>
  19. <JobDetail>
  20. <Name>Job3</Name>
  21. <JobType>TianWei.TaskScheduler.Jobs.Job3</JobType>
  22. <CronExpression>06 36 14 * * 2</CronExpression>
  23. <Enabled>true</Enabled>
  24. <StartTime>2015-05-20 12:00:00</StartTime>
  25. <EndTime>2015-05-30 12:00:00</EndTime>
  26. </JobDetail>
  27. <JobDetail>
  28. <Name>Job4</Name>
  29. <JobType>TianWei.TaskScheduler.Jobs.Job3</JobType>
  30. <CronExpression>08 35 14 26 05 *</CronExpression>
  31. <Enabled>true</Enabled>
  32. <StartTime>2015-05-20 12:00:00</StartTime>
  33. <EndTime>2015-05-30 12:00:00</EndTime>
  34. </JobDetail>
  35. </ArrayOfJobDetail>
  36. <!--自定义Cron 秒 分 时 日 月 周 当周不为*时 月和日不生效-->

第五增加如下代码来初使化

  1. static void Main(string[] args)
  2. {
  3. Scheduler sc = new Scheduler();
  4. sc.Execute();
  5. Console.ReadKey();
  6. }

第六运行程序如果Job中有输出就可以看到效果

四、自定义Crom解释

这里的Cron表达式也是个山寨的,自定义的,本想解析Quartz的表达式,但是感觉太复杂了

表达式一共六位组成

第一位:秒 只能是0-59或*

第二位:分 只能是0-59或*

第三位:小时 只能是0-24或*

第四位:日 只能是0-31或* 每天执行为0

第五位:月 只能是0-12或*

第六位:周 只能是0-6或*

注:当第六位不为*时第三四五位失效

例:

5 0 0 * * *  每隔五秒执行

5 2 1 * * *  每隔一小时两分钟五秒执行

5 37 14 0 * * 每天的14:37:5执行

6 36 14 * * 2 每周二的14:36:6执行

6 36 14 20 6 * 每年6月20号14:36:6执行

6 36 14 20 0 * 每月20号14:36:6执行

代码地址:https://github.com/hantianwei/TaskScheduler

如果有好的改动或是意见请反馈给我,代码改动后也回传我一份,谢谢

Quartz.Net任务统一调度框架的更多相关文章

  1. 山寨版Quartz.Net任务统一调度框架

    TaskScheduler 在日常工作中,大家都会经常遇到Win服务,在我工作的这些年中一直在使用Quartz.Net这个任务统一调度框架,也非常好用,配置简单,但是如果多个项目组的多个服务部署到一台 ...

  2. Quartz.net 开源job调度框架(二)----定点执行

    在上一篇  Quartz.net 开源job调度框架(一) 中讲到了基本的使用以及配置job轮训数据执行 这种做法适用于对数据操作实时性要求不高的场景,在实际场景中还有一种比较常用的场景就是我们需要在 ...

  3. Quartz.net 开源job调度框架(一)

    Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等. Quartz.NET允许开发人员根据时间间隔(或天)来调度作业.它实现了作业和 ...

  4. 开源调度框架Quartz最佳实践

    开源调度框架Quartz最佳实践 Quartz是一个Java调度框架,当前的最新版本为2.2.1. 以Quartz 2.2.1版为例,Quartz最佳实践(用于生产系统)总结如下: 1.跳过更新检查Q ...

  5. Quartz定时调度框架

    Quartz定时调度框架CronTrigger时间配置格式说明 CronTrigger时间格式配置说明 CronTrigger配置格式: 格式: [秒] [分] [小时] [日] [月] [周] [年 ...

  6. Quartz.net(调度框架) 使用Mysql作为存储

    最近公司的做的项目中涉及到配置任务地址然后按照配置去目标地址提取相关的数据,所以今天上午在Internet上查看有关定时任务(调度任务)的相关信息,筛选半天然后查找到Quartz.net. Quart ...

  7. Quartz.Net 调度框架配置介绍

    在平时的工作中,估计大多数都做过轮询调度的任务,比如定时轮询数据库同步,定时邮件通知等等.大家通过windows计划任务,windows服务等都实现过此类任务,甚至实现过自己的配置定制化的框架.那今天 ...

  8. Quartz基础调度框架-第二篇服务

    很多应用场景Quartz运行于Windows服务 Conf 在这个基本结构里 是用来存放配置  和上一篇 控制台运行的一样的结构 jobs.xml 的配置清单 <!-- 任务配置--> & ...

  9. Quartz基础调度框架-第一篇控制台

    Quartz基础调度框架 Quartz核心的概念:scheduler任务调度.Job任务.Trigger触发器.JobDetail任务细节 结构 Conf 在这个基本结构里 是用来存放配置 publi ...

随机推荐

  1. cocos2d-x路~使得第一个字游戏(一个)

    前言 去年的回忆.另外,在第三.他们开发了他们的第一场比赛四月,它是游戏.所以我决定走上独立开发的道路上.了.第一款游戏达到它应有的盈利水平.然而这款游戏开发后的时间里.都没再取得还有一款令自己惬意的 ...

  2. Best Time to Buy and Sell Stock I,II,III [leetcode]

    Best Time to Buy and Sell Stock I 你只能一个操作:维修preMin拍摄前最少发生值 代码例如以下: int maxProfit(vector<int> & ...

  3. HDU 1018-Big Number(数学)

    Big Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total ...

  4. TestNg显示器(一个)-----监听器,类型和配置使用---另外META-INF详细解释

    原创文章,版权所有所有.转载,归因:http://blog.csdn.net/wanghantong/article/details/40404939 TestNg提供了听众和拦截多种接口开发我们自己 ...

  5. poj2112 Optimal Milking --- 最大流量,二分法

    nx一个挤奶器,ny奶牛,每个挤奶罐为最m奶牛使用. 现在给nx+ny在矩阵之间的距离.要求使所有奶牛挤奶到挤奶正在旅程,最小的个体奶牛步行距离的最大值. 始感觉这个类似二分图匹配,不同之处在于挤奶器 ...

  6. 图片切割工具---产生多个div切割图片 采用for和一的二维阵列设置背景位置

    照片库 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhb21vZ2c=/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  7. Windows Server 2012启用Windows功能NetFx3时出错解决方法

    作者:冰点阳光 | 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址:http://baohua.me/operating-system/windows-server-2012- ...

  8. Cocos2d Lua 越来越小样本 内存游戏

    1.游戏简介 一个"记忆"类的比赛游戏.你和电脑对战,轮到谁的回合,谁翻两张牌,假设两张牌一样.就消掉这两张牌,得2分,能够继续翻牌,假设两张牌不一样,就换一个人.直到最后.看谁的 ...

  9. 简单实现Android平台多语言

    这里,我们认识到两种语言.中国简体和繁体中国. 在res文件建议两个文件夹 values-zh-rCN values-zh-rTW 两个目录下都有一个strings.xml文件. 两个同名文件的字符串 ...

  10. HDU 5012 Dice (BFS)

    事实上是非常水的一道bfs,用字符串表示每一个状态,map判重就ok了. 题目链接:http://acm.hdu.edu.cn/showproblem.php? pid=5012 #include&l ...