• qurtz.net


    Quartz.NET的使用(附源码)(作者 陈珙)

     

    简介

      虽然Quartz.NET被园子里的大神们写烂了,自己还是整理了一篇,结尾会附上源码地址。

      Quartz.NET是一款功能齐全的开源作业调度框架,小至的应用程序,大到企业系统都可以适用。Quartz是作者James House用JAVA语言编写的,而Quartz.NET是从Quartz移植过来的C#版本。

      在一般企业,可以利用Quartz.Net框架做各种的定时任务,例如,数据迁移、跑报表等等。

      另外还有一款Hangfire https://www.hangfire.io/,也是作业调度框架,有自带监控web后台,比Quartz.Net更加易用,简单。但是Cron最低只支持到分钟级。然而Hangfire不是今天的主角,有机会再介绍。

    简单例子

    新建一个控制台项目,通过Nuget管理下载Quartz包

    using System;
    using System.Collections.Specialized;
    using Quartz;
    using Quartz.Impl;
    
    namespace QuartzDotNetDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                //创建一个调度器工厂
                var props = new NameValueCollection
                {
                    { "quartz.scheduler.instanceName", "QuartzDotNetDemo" }
                };
                var factory = new StdSchedulerFactory(props);
    
                //获取调度器
                var sched =  factory.GetScheduler();
                sched.Start();
    
                //定义一个任务,关联"HelloJob"
                var job = JobBuilder.Create<HelloJob>()
                    .WithIdentity("myJob", "group1")
                    .Build();
    
                //由触发器每40秒触发执行一次任务
                var trigger = TriggerBuilder.Create()
                    .WithIdentity("myTrigger", "group1")
                    .StartNow()
                    .WithSimpleSchedule(x => x
                        .WithIntervalInSeconds(40)
                        .RepeatForever())
                    .Build();
    
                 sched.ScheduleJob(job, trigger);
            }
        }
    
        public class HelloJob : IJob
        {
            public void Execute(IJobExecutionContext context)
            {
                Console.WriteLine("你好");
            }
        }
    }

    一个简单的调度任务流程如下:

    概念

      有几个重要类和概念需要了解一下:

    • IScheduler - 与调度器交互的主要API.
    • IJob -由执行任务实现的接口。
    • IJobDetail - 定义Job实例
    • ITrigger - 按照定义的时间让任务执行的组件.
    • JobBuilder - 用于定义或者创建JobDetai
    • TriggerBuilder -用于定义或生成触发器实例

      他们之间的关系大概如下:

      当有空闲线程同时,到了该执行的时间,那么就会由Trigger去触发绑定的Job执行它的Excute方法,假如这次没执行完,却到了下一次的运行时间,如果有空闲线程就仍然会再次执行。但是如果没有空闲线程,会等到腾出空闲的线程才会执行,但是超过quartz.jobStore.misfireThreshold设置的时间就会放弃这次的运行。

      当然也可以在Job贴上DisallowConcurrentExecution标签让Job进行单线程跑,避免没跑完时的重复执行。

    改造

      在第一个简单的demo里是无法良好的在实际中使用,因此我们需要改造一下。

        需要的第三方包:

    • Autofac version="4.6.2"
    • Autofac.Extras.Quartz version="3.4.0"
    • Common.Logging version="3.4.1"
    • Common.Logging.Core version="3.4.1"
    • Common.Logging.Log4Net1213 version="3.4.1"
    • log4net version="2.0.3"
    • Newtonsoft.Json version="10.0.3"
    • Quartz version="2.6.1"
    • Topshelf version="4.0.3"
    • Topshelf.Autofac version="3.1.1"
    • Topshelf.Log4Net version="3.2.0"
    • Topshelf.Quartz version="0.4.0.1"

    Topshelf

      Topshelf是一款为了方便安装部署在Windows系统下而诞生的宿主框架,它基于控制台项目,为开发人员带来更方便的调试和部署。

      官网:https://topshelf.readthedocs.io/en/latest/index.html

      那我们可以在Program.cs里写入以下代码:

    using Topshelf;
    using Topshelf.Autofac;
    
    namespace QuartzDotNetDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                HostFactory.Run(config =>
                {
                    config.SetServiceName(JobService.ServiceName);
                    config.SetDescription("Quartz.NET的demo");
                    config.UseLog4Net();
                    config.UseAutofacContainer(JobService.Container);
    
                    config.Service<JobService>(setting =>
                    {
                        JobService.InitSchedule(setting);
                        setting.ConstructUsingAutofacContainer();
                        setting.WhenStarted(o => o.Start());
                        setting.WhenStopped(o => o.Stop());
                    });
                });
            }
        }
    }

    JobService

      此类用来读取配置信息、初始化调度任务和注入ioc容器

    public class JobService
        {
            #region 初始化
            private static readonly ILog Log = LogManager.GetLogger(typeof(JobService));
    
            private const string JobFile = "JobsConfig.xml";
            private static readonly string JobNamespceFormat;
            public static readonly string ServiceName;
            private static readonly Jobdetail[] JobList;
            public static IContainer Container;
    
            static JobService()
            {
                var job = JobFile.XmlToObject<JobsConfig>();
    
                ServiceName = job.Quartz.ServiceName;
                JobNamespceFormat = job.Quartz.Namespace;
                JobList = job.Quartz.JobList.JobDetail;
    
                Log.Info("Jobs.xml 初始化完毕");
    
                InitContainer();
            } 
            #endregion
    
            /// <summary>
            /// 初始化调度任务
            /// </summary>
            /// <param name="svc"></param>
            public static void InitSchedule(ServiceConfigurator<JobService> svc)
            {
                svc.UsingQuartzJobFactory(Container.Resolve<IJobFactory>);
    
                foreach (var job in JobList)
                {
                    svc.ScheduleQuartzJob(q =>
                    {
                        q.WithJob(JobBuilder.Create(Type.GetType(string.Format(JobNamespceFormat, job.JobName)))
                            .WithIdentity(job.JobName, ServiceName)
                            .Build);
    
                        q.AddTrigger(() => TriggerBuilder.Create()
                            .WithCronSchedule(job.Cron)
                            .Build());
    
                        Log.InfoFormat("任务 {0} 已完成调度设置", string.Format(JobNamespceFormat, job.JobName));
                    });
                }
    
                Log.Info("调度任务 初始化完毕");
            }
    
            /// <summary>
            /// 初始化容器
            /// </summary>
            private static void InitContainer()
            {
                var builder = new ContainerBuilder();
                builder.RegisterModule(new QuartzAutofacFactoryModule());
                builder.RegisterModule(new QuartzAutofacJobsModule(typeof(JobService).Assembly));
                builder.RegisterType<JobService>().AsSelf();
    
                var execDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                var files = Directory.GetFiles(execDir, "QuartzDotNetDemo.*.dll", SearchOption.TopDirectoryOnly);
                if (files.Length > 0)
                {
                    var assemblies = new Assembly[files.Length];
                    for (var i = 0; i < files.Length; i++)
                        assemblies[i] = Assembly.LoadFile(files[i]);
    
                    builder.RegisterAssemblyTypes(assemblies)
                        .Where(t => t.GetInterfaces().ToList().Contains(typeof(IService)))
                        .AsSelf()
                        .InstancePerLifetimeScope();
                }
    
                Container = builder.Build();
                Log.Info("IOC容器 初始化完毕");
            }
    
            public bool Start()
            {
                Log.Info("服务已启动");
                return true;
            }
    
            public bool Stop()
            {
                Container.Dispose();
                Log.Info("服务已关闭");
                return false;
            }
        }

    触发器类型

      一共有4种:

    • WithCalendarIntervalSchedule
    • WithCronSchedule
    • WithDailyTimeIntervalSchedule
    • WithSimpleSchedule

      在项目中使用的是WithCronSchedule,因为cron表达式更加灵活、方便。

    Cron表达式

    字段名是否必填值范围特殊字符
    Seconds YES 0-59 , - * /
    Minutes YES 0-59 , - * /
    Hours YES 0-23 , - * /
    Day of month YES 1-31 , - * ? / L W
    Month YES 1-12 or JAN-DEC , - * /
    Day of week YES 1-7 or SUN-SAT , - * ? / L #
    Year NO empty, 1970-2099 , - * /

    例子:

      "0 0/5 * * * ?"    ---- 每5分钟触发一次

      "10 0/5 * * * ?"   -----每5分钟触发一次,每分钟10秒(例如:10:00:10 am,10:05:10,等等)

      "0 0/30 8-9 5,20 * ?" ----在每个月的第5到20个小时之间,每隔半小时就会触发一个触发点。请注意,触发器不会在上午10点触发,仅在8点,8点30分,9点和9点30分

    BaseJob

      我们定义一个BaseJob写入公共处理逻辑,例如:业务逻辑禁用、公共异常日志消息推送等等。再由具体的Job去继承重写基类的ExecuteJob,简单的适配器模式运用。

    public abstract class BaseJob : IJob
        {
            protected readonly CommonService CommonService;
    
            protected BaseJob(CommonService commonService)
            {
                CommonService = commonService;
            }
    
            public void Execute(IJobExecutionContext context)
            {
                //公共逻辑
                CommonService.Enabled();
    
                //job逻辑
                ExecuteJob(context);
            }
    
            public abstract void ExecuteJob(IJobExecutionContext context);
        }

    结束

      最后按照惯例双手奉上demo源码。https://github.com/SkyChenSky/QuartzDotNetDemo.git

      如果错误麻烦在下面评论指出,我会及时修改。

     作  者: 陈珙 
    出  处:http://www.cnblogs.com/skychen1218/ 
    关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教! 
    版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。 
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力

     
  • 相关阅读:
    2011 年50+优秀的网页设计(下)
    25+令人惊讶的是令人难以置信的WordPress技术支持网站
    2011 漂亮的网站(上)
    2011 美丽的网站(下)
    38 jQuery和CSS多级下拉菜单解决方案(一)
    HTMl代码片段
    30 +漂亮的jQuery菜单导航(一)
    分享几套还算不错的后台模板(有源码下载)
    配 色 方 案
    一个小故事
  • 原文地址:https://www.cnblogs.com/zengpeng/p/9329028.html
Copyright © 2020-2023  润新知