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

 

TaskScheduler

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

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

一、技术准备

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

二、框架搭建

第一我们建立项目TianWei.TaskScheduler

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

    /// <summary>
/// 自定义Timer
/// </summary>
public class TWTimer : System.Timers.Timer
{
public JobDetail JobDetail { get; set; }
}

第三建立JobDetail

  /// <summary>
/// 作业请情
/// </summary>
[XmlRootAttribute("xml", IsNullable = false)]
public class JobDetail
{
/// <summary>
/// 作业名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 作业执行类
/// </summary>
public string JobType { get; set; } /// <summary>
/// 自定义Cron表达式
/// </summary>
public string CronExpression { get; set; } /// <summary>
/// 作业类型
/// </summary>
[XmlIgnoreAttribute]
public WorkType WorkType { get; set; } /// <summary>
/// 如果是每周 周几
/// </summary>
[XmlIgnoreAttribute]
public DayOfWeek Week { get; set; } /// <summary>
/// 执行表达式
/// </summary>
[XmlIgnoreAttribute]
public string ExecuteExpression { get; set; } /// <summary>
/// 执行间隔 循环执行有效
/// </summary>
[XmlIgnoreAttribute]
public int Interval { get; set; } /// <summary>
/// 作业状态 停启用
/// </summary>
public bool Enabled { get; set; } /// <summary>
/// 作业开始工作时间- 可为空
/// </summary>
public string StartTime { get; set; } /// <summary>
/// 作业结束时间-可为空
/// </summary>
public string EndTime { get; set; } /// <summary>
/// 作业开始工作时间-默认为最小时间
/// </summary>
[XmlIgnoreAttribute]
public DateTime JobStartTime
{
get
{
DateTime value = DateTime.MinValue;
if (StartTime != null)
{
DateTime.TryParse(StartTime, out value);
}
return value;
}
set { }
} /// <summary>
/// 作业结束工作时间-默认为最大时间
/// </summary>
[XmlIgnoreAttribute]
public DateTime JobEndTime
{
get
{
DateTime value = DateTime.MaxValue;
if (EndTime != null)
{
DateTime.TryParse(EndTime, out value);
}
return value;
}
set { }
}
}

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

    public class Job
{
public void Execute(JobDetail jobDetail, IJob job)
{
if (!jobDetail.Enabled) return;
if (DateTime.Now < jobDetail.JobStartTime || DateTime.Now > jobDetail.JobEndTime) return;
if (jobDetail.WorkType == WorkType.Week)
{
if (jobDetail.Week == DateTime.Now.DayOfWeek && jobDetail.ExecuteExpression == DateTime.Now.ToString("HHmmss"))
{
job.Execute();
}
}
else if (jobDetail.WorkType == WorkType.Yearly)
{
if (jobDetail.ExecuteExpression == DateTime.Now.ToString("MMddHHmmss"))
{
job.Execute();
}
}
else if (jobDetail.WorkType == WorkType.Monthly)
{
if (jobDetail.ExecuteExpression == DateTime.Now.ToString("ddHHmmss"))
{
job.Execute();
}
}
else if (jobDetail.WorkType == WorkType.Daily)
{
if (jobDetail.ExecuteExpression == DateTime.Now.ToString("HHmmss"))
{
job.Execute();
}
}
else if (jobDetail.WorkType == WorkType.Loop)
{
job.Execute();
}
} }

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

   /// <summary>
/// 作业接口
/// </summary>
public interface IJob
{
/// <summary>
/// 作业需要继承的接口
/// </summary>
void Execute(); }

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

  public class Scheduler
{
[ImportMany(typeof(IJob))]
public List<IJob> jobs;
public Dictionary<string, IJob> dicJobs;
public Dictionary<string, TWTimer> dicTimer;
private void Run()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog(Environment.CurrentDirectory));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
} public void Execute()
{
Run();
SetDicJobs();
SetDicTimers();
FileWatcher();
} private void SetDicJobs()
{
if (jobs != null)
{
dicJobs = new Dictionary<string, IJob>();
foreach (var job in jobs)
{
dicJobs.Add(job.ToString(), job);
}
}
} private void SetDicTimers()
{
dicTimer = new Dictionary<string, TWTimer>();
var jobList = (List<JobDetail>)XmlHelper.XmlDeserialize(typeof(List<JobDetail>), Config.ConfigPath);
if (jobList != null)
{
foreach (var item in jobList)
{
SetTimer(item);
}
}
} /// <summary>
/// Timer
/// </summary>
/// <param name="jobDetail"></param>
private void SetTimer(JobDetail jobDetail)
{
TWTimer timer = new TWTimer();
timer.JobDetail = CronHelper.SetCron(jobDetail);
if (timer.JobDetail.WorkType == WorkType.Loop)
{
timer.Interval = timer.JobDetail.Interval;
}
else
{
timer.Interval = 1000;
}
timer.AutoReset = true;
timer.Enabled = true;
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
dicTimer.Add(timer.JobDetail.Name, timer);
} /// <summary>
/// Timer事件
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
try
{
var timer = (TWTimer)source;
if (dicJobs.Any(o => o.Key == timer.JobDetail.JobType))
{
Job job = new Job();
job.Execute(timer.JobDetail, dicJobs[timer.JobDetail.JobType]);
}
}
catch (Exception ex)
{
//记录日志
}
} /// <summary>
/// 文件监听
/// </summary>
private void FileWatcher()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Environment.CurrentDirectory;
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = Config.ConfigFile;
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
} /// <summary>
/// 文件改动事件
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void OnChanged(object source, FileSystemEventArgs e)
{
var jobList = (List<JobDetail>)XmlHelper.XmlDeserialize(typeof(List<JobDetail>), Config.ConfigPath);
if (jobList != null)
{
foreach (var item in jobList)
{
if (dicTimer.Any(o => o.Key == item.Name))
{
var timer = dicTimer[item.Name];
if (item.JobType != timer.JobDetail.JobType || item.CronExpression != timer.JobDetail.CronExpression)
{
timer.JobDetail = CronHelper.SetCron(item);
if (timer.JobDetail.WorkType == WorkType.Loop)
{
timer.Interval = timer.JobDetail.Interval;
}
else
{
timer.Interval = 1000;
}
}
timer.JobDetail.Enabled = item.Enabled;
timer.JobDetail.StartTime = item.StartTime;
timer.JobDetail.EndTime = item.EndTime;
}
else
{
SetTimer(item);
}
}
}
}
}

其它辅助类详见源码

三、使用方法

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

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

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

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

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

<?xml version="1.0"?>
<ArrayOfJobDetail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<JobDetail>
<Name>Job1</Name>
<JobType>TianWei.TaskScheduler.Jobs.Job1</JobType>
<CronExpression>1 00 00 * * *</CronExpression>
<Enabled>true</Enabled>
<StartTime>2015-05-1 12:00:00</StartTime>
<EndTime>2015-05-30 12:00:00</EndTime>
</JobDetail>
<JobDetail>
<Name>Job2</Name>
<JobType>TianWei.TaskScheduler.Jobs.Job2</JobType>
<CronExpression>05 37 14 0 * *</CronExpression>
<Enabled>true</Enabled>
<StartTime></StartTime>
<EndTime>2015-05-30 12:00:00</EndTime>
</JobDetail>
<JobDetail>
<Name>Job3</Name>
<JobType>TianWei.TaskScheduler.Jobs.Job3</JobType>
<CronExpression>06 36 14 * * 2</CronExpression>
<Enabled>true</Enabled>
<StartTime>2015-05-20 12:00:00</StartTime>
<EndTime>2015-05-30 12:00:00</EndTime>
</JobDetail>
<JobDetail>
<Name>Job4</Name>
<JobType>TianWei.TaskScheduler.Jobs.Job3</JobType>
<CronExpression>08 35 14 26 05 *</CronExpression>
<Enabled>true</Enabled>
<StartTime>2015-05-20 12:00:00</StartTime>
<EndTime>2015-05-30 12:00:00</EndTime>
</JobDetail>
</ArrayOfJobDetail>
<!--自定义Cron 秒 分 时 日 月 周 当周不为*时 月和日不生效-->

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

 static void Main(string[] args)
{
Scheduler sc = new Scheduler();
sc.Execute();
Console.ReadKey();
}

第六运行程序如果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. Android使用Activity用作弹出式对话框

    转载请表明出处:http://blog.csdn.net/lmj623565791/article/details/23116115 Android中可用于实现对话框的有Dialog,PopupWin ...

  2. WPF技术触屏上的应用系列(五): 图片列表异步加载、手指进行缩小、放大、拖动 、惯性滑入滑出等效果

    原文:WPF技术触屏上的应用系列(五): 图片列表异步加载.手指进行缩小.放大.拖动 .惯性滑入滑出等效果 去年某客户单位要做个大屏触屏应用,要对档案资源进行展示之用.客户端是Window7操作系统, ...

  3. 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern)

    原文:乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pa ...

  4. Eclipse在Jar形成和应用程序包

    最近的熟悉Java语言.在学习过程中Eclipse经常使用再熟悉它.本文简单说下Jar形成和应用程序包. Java在Jar相当于包C/C++该lib库,它是.class文件打包:经常使用Jar包有AP ...

  5. S如何解决安卓DK无法下载Package问题

    安装一些用户Android SDK后.打开Android SDK Manager下载API当总是显示"Done loading packages"却迟迟不能前进.自己也出现了这样的 ...

  6. Android-管理Activity生命周期 -重新创建Activity

    按照正常的app行为,很少情况下activity会销毁,只有当用户点击了返回按钮或者activity通过调用finish()发出销毁信号.系统也有可能销毁activity如果它是停止状态并且很久没有使 ...

  7. JS达到Web指定保存的和打印功能的内容

    背景 首先,说说文章的背景.近期手中的一个项目,因为需求中要求提供Web界面的打印功能.当然假设没有打印机,还能够提供保存到本地.项目组长把这个"小任务"分给了我.本着努力为组长分 ...

  8. UVA 1358 - Generator(dp+高斯消元+KMP)

    UVA 1358 - Generator option=com_onlinejudge&Itemid=8&page=show_problem&category=524& ...

  9. ExtJS4 表格的嵌套 rowExpander

    今天做一个grid,里面的数据须要带明细,思来想去还是搞个表格嵌套吧!看下图 对于grid中每一条记录点击左边的+号能展开一个明细的子表格 全部数据包含列名均从后台获得,子表格的数据临时在本地以做測试 ...

  10. Cordova WP8 平台安装部署

    原文:Cordova WP8 平台安装部署 Cordova是一个开放源码移动开发框架. 它允许您使用标准的 web 技术如 HTML5. CSS3 和 JavaScript 进行跨平台开发,避免每个移 ...