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

简介: TaskScheduler 在日常工作中,大家都会经常遇到Win服务,在我工作的这些年中一直在使用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

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

目录
相关文章
|
1月前
|
设计模式 开发框架 JavaScript
基于.NET8 + Vue/UniApp前后端分离的快速开发框架,开箱即用!
基于.NET8 + Vue/UniApp前后端分离的快速开发框架,开箱即用!
|
13天前
|
消息中间件 开发框架 监控
NET任务调度框架Hangfire使用指南
Hangfire 是一个用于 .NET 应用程序的开源任务调度框架,支持长时间运行任务、定时任务等。通过简单的安装配置,即可将任务从主线程分离,提升应用性能。支持多种数据库,提供丰富的任务类型如立即执行、延迟执行和周期性任务,并有可视化管理界面 Hangfire Dashboard。还支持安全性配置及扩展插件,如 Hangfire.HttpJob,适合各种复杂场景下的任务调度需求。
40 1
NET任务调度框架Hangfire使用指南
|
1月前
|
开发框架 安全 .NET
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱。它不仅加速了应用开发进程,提升了开发质量和可靠性,还促进了创新和业务发展,培养了专业人才和技术社区,为软件开发和数字化转型做出了重要贡献。
25 5
|
1月前
|
传感器 人工智能 供应链
.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。
本文深入探讨了.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。通过企业级应用、Web应用及移动应用的创新案例,展示了.NET在各领域的广泛应用和巨大潜力。展望未来,.NET将与新兴技术深度融合,拓展跨平台开发,推动云原生应用发展,持续创新。
32 4
|
1月前
|
开发框架 .NET C#
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位。从企业应用到电子商务,再到移动开发,.NET 均展现出卓越性能,助力开发者提升效率与项目质量,推动行业持续发展。
29 4
|
1月前
|
开发框架 Java .NET
.net core 非阻塞的异步编程 及 线程调度过程
【11月更文挑战第12天】本文介绍了.NET Core中的非阻塞异步编程,包括其基本概念、实现方式及应用示例。通过`async`和`await`关键字,程序可在等待I/O操作时保持线程不被阻塞,提高性能。文章还详细说明了异步方法的基础示例、线程调度过程、延续任务机制、同步上下文的作用以及如何使用`Task.WhenAll`和`Task.WhenAny`处理多个异步任务的并发执行。
|
1月前
|
消息中间件 监控 数据可视化
基于.NET开源、功能强大且灵活的工作流引擎框架
基于.NET开源、功能强大且灵活的工作流引擎框架
|
1月前
|
开发框架 网络协议 .NET
C#/.NET/.NET Core优秀项目和框架2024年10月简报
C#/.NET/.NET Core优秀项目和框架2024年10月简报
|
1月前
|
网络协议 Unix Linux
精选2款C#/.NET开源且功能强大的网络通信框架
精选2款C#/.NET开源且功能强大的网络通信框架
|
1月前
|
开发框架 JavaScript 前端开发
2024年全面且功能强大的.NET快速开发框架推荐,效率提升利器!
2024年全面且功能强大的.NET快速开发框架推荐,效率提升利器!