Quartz.NET总结(六)了解Jobs 和 Triggers

前面讲了一些Quartz.net 的一些东西, http://www.cnblogs.com/zhangweizhong/category/771057.html

但是发现有一些基础的东西,没有说到。Quartz.net 里面一些个基本的元素,比如Jobs,Triggers等。

Quartz API 的接口和类主要有:

  1. IScheduler – scheduler调度的主要接口api
  2. IJob – scheduler调度执行job需要实现的接口api
  3. IJobDetail – 用来定义jobs的实例
  4. ITrigger – 定义需要执行的job的调度构成
  5. JobBuilder – 用于定义/创建job的明细情况,定义job的实例
  6. TriggerBuilder – 用于定义/创建Trigger的实例

一个Scheduler调度的生命周期从开始创建,到结束关闭,都是通过SchedulerFactory 实现的。当我们创建了IScheduler接口后,我们就可以用来add、remove、或者list jobs和triggers,或者是其他与调度相关的工作。

Scheduler 调度的方法有:

  1. WithCalendarIntervalSchedule
  2. WithCronSchedule
  3. WithDailyTimeIntervalSchedule
  4. WithSimpleSchedule

使用DateBuilder可以轻松的构建特定的执行时间点等,如下个小时、十点钟、或者是其他的具体时间方式。

Jobs and Triggers

定义一个job必须实现了IJob接口,如下为IJob接口的方法:

namespace Quartz
{
    public interface IJob
    {
        void Execute(JobExecutionContext context);
    }
}

当 Job的trigger被触发后,scheduler调度的线程 就会执行 Execute 方法,同时 JobExecutionContext 包含运行环境参数等信息也会传递给execute方法。

比如说:job可以创建并存储在独立的scheduler调度中并由Trigger触发,不同的Trigger触发器可以触发相同的job任务。这种松耦合能够保证如果Job的触发规则变了,不用去该job,而只需修改某个Trigger的触发规则。同时,配置在scheduler调度中的job任务任然是可以被其他触发器使用,而不需要我们重新定义新的job任务。

接下来需要我们了解知道的job深入内容主要有IJob接口的Execute()方法和JobDetails。

对于一个job任务来说,我们知道如何制定特定type类型和如何编写实现一个job的代码,对于使用Quartz.NET来说我们需要了解它各种各样的属性参数,可以通过job的JobDetail明细类来进行分析。

JobDetail明细类的实例是通过JobBuilder创建的,JobBuilder允许我们使用连续的接口方式进行实现和编码。

        // define the job and tie it to our HelloJob class
        IJobDetail job = JobBuilder.Create()
          .WithIdentity("myJob", "group1") // name "myJob", group "group1"
          .Build();

        // Trigger the job to run now, and then every 40 seconds
        ITrigger trigger = TriggerBuilder.Create()
          .WithIdentity("myTrigger", "group1")
          .StartNow()
          .WithSimpleSchedule(x => x
            .WithIntervalInSeconds(40)
            .RepeatForever())
          .Build();

        // Tell quartz to schedule the job using our trigger
        sched.scheduleJob(job, trigger);

接下来定义一个job实现类HelloJob:

    public class HelloJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            Console.WriteLine("HelloJob is executing.");
        }
    }

说到这里,有些 job任务是需要设置一些属性或者参数的 ,怎么给运行中的Job赋值呢? JobDataMap 提供了这方面的实现,也就是在 JobDetail对象中,设置job需要的值或参数。

JobDataMap

JobDataMap可以存储容纳任意数量的可序列化对象,以便在job实例化执行的时候使用。JobDataMap是IDictionary接口的实现,并添加了一些方便存储和检索原始类型数据的方法。

下面演示一小段代码来说明将job任务添加到scheduler调度前先将数据添加到JobDataMap:

Setting Values in a JobDataMap

  // define the job and tie it to our DumbJob class
            IJobDetail job = JobBuilder.Create()
               .WithIdentity("myJob", "group1") // name "myJob", group "group1"
                  .UsingJobData("jobSays", "Hello World!")
               .UsingJobData("myFloatValue", 3.141f)
               .Build();

下面演示下job任务执行时候从JobDataMap中取出数据的代码片段:

Getting Values from a JobDataMap

  public class DumbJob : IJob
    {
        public void Execute(JobExecutionContext context)
        {
            JobKey key = context.JobDetail.Key;

            JobDataMap dataMap = context.JobDetail.JobDataMap;

            string jobSays = dataMap.GetString("jobSays");
            float myFloatValue = dataMap.GetFloat("myFloatValue");

            Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
        }
    }

有了JobDataMaps Job在执行的过程中,就可以取出需要的相关参数。

 

 

Quartz.NET总结(五)基于Quartz.net 的开源任务管理平台

前面总结了很多,关于Quartz.net 的文章,介绍了如何使用Quartz.net。不清楚的朋友,可以看我之前的系列文章,http://www.cnblogs.com/zhangweizhong/category/771057.html 。

最近,又重新整理,开发出了一套基于Quartz.net 的任务管理平台。将Quartz.net 的任务调度,管理等功能统一整合,形成了一套比较完整的任务调度平台。主要是:任务调度服务,后台任务管理 等功能。

  github地址:https://github.com/weizhong1988/Weiz.TaskManager

 

  1.  系统简介

Quartz.net是一个开源的任务调度工具,相当于数据库中的 Job、Windows 的计划任务、Unix/Linux 下的 Cron,但 Quartz 可以把排程控制的更精细,对任务调度的领域问题进行了高度的抽象,实现作业的灵活调度。

任务管理平台通过window服务来集成Quartz.net 实现作业的调度,只需要修改配置文件和添加相应Job即可完成作业添加,使用简单方便。

 

 

  2.  项目结构

    系统目前包含八个项目组成:

Weiz.TaskManager.HouTai_New                  1. 任务后台管理系统,任务,。

Weiz.TaskManager.Tasks                        2. 所有作业任务的集合,目前只有一个TestJob。

Weiz.TaskManager.ServiceBus                    3. 集成Quartz.net 的 window服务,通过window服务来承载调度Weiz.TaskManager.Tasks 的各个任务Job。

Weiz.TaskManager.TaskUtility                     4. 操作任务的公共类库。

Weiz.TaskManager.Utility                           5. 整个平台的公共类库

Weiz.TaskManager.Models

Weiz.TaskManager.BLL

Weiz.TaskManager.DAL

 

  3.  数据库    

1. 任务管理平台,有基于数据库方式和基于xml 配置文件的方式。

a)基于数据库方式,主要是将全部的任务的配置,还有任务的运行情况等数据,保存在数据库。

b)基于xml 配置文件的方式,则是将全部的任务配置保存在xml 文件中。

2. 在\Documents 目录下 找到”SQL合并脚本_20150911.sql”执行创建相关表和初始数据。

 

  4.  系统配置

系统中所有的作业信息,都存储在数据库或是配置文件中。window 宿主服务启动时,会自动读取相应的配置的job任务,完成任务的初始化和调度。

所有项目的配置文件,都在Config\Config.config 下配置。 设置数据库或是配置文件的存储方式,请在相关项目下增加如下配置:

   
   "StorageMode" value="2"/>

 

  5.  其他

Weiz.TaskManager 任务平台,支持数据库配置和配置文件配置两种方式。如果不需要后台管理端,也可以直接使用 window 服务 + 配置文件的模式。

a)基于数据库方式,主要是将全部的任务的配置,还有任务的运行情况等数据,保存在数据库。可以部署后台管理系统,

b)基于xml 配置文件的方式,则是将全部的任务配置保存在xml 文件中。采用直接使用 window 服务 + 配置文件的方式 非常简单方便。无需配置相关的数据库和部署后台管理系统。

如果部署的任务不多的话,可以用这种简单的模式。

 

 6. 部分截图

后台管理:

 

宿主服务:

 

 

7. 最后

1. 这个是本人的第一个开源项目。有很多不足,其中,也有部分代码,借鉴其他朋友的经验,大家多多包涵。

2. 这只是个介绍,没有详细的说明如何使用。如有问题,可以找我详细了解。

 

Quartz.NET总结(四)Quartz 远程调度

前面篇已经介绍了Quartz.NET的配置,使用和Cron表达式表达式的写法。基本上后台的定时任务的定时执行已经完成,并能正确的按照执行计划,执行相关的job 。

然后,如果任务需要更新,停止某个任务呢 ? 总不能上服务器去改相关job 的配置吧。 所以,Quartz.NET 也给我们提供了远程调度的方法。这次就简单介绍下Quartz.NET 远程调度的配置和方法。

1. 配置Quartz.NET 远程方式执行

计划任务的服务器上配置远程调用Quartz的出口, quartz.config 增加如下配置即可

      #export this server to remoting context
      quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
      quartz.scheduler.exporter.port = 555
      quartz.scheduler.exporter.bindName = QuartzScheduler
      quartz.scheduler.exporter.channelType = tcp
      quartz.scheduler.exporter.channelName = httpQuartz

几点说明:
1. 客户端的”quartz.scheduler.proxy.address” 配置的地址由服务器上配置的channelType,port和 bindName 等组成。注意保持一致。

2. 服务器初始化启动时,从数据库启动相关Job 的过程这里不细说。下次介绍我自己做的任务调度平台的时候,再细说。

3. Job 服务器上,其实就算一个服务宿主,在后台控制调度全部的job 执行计划。

  2. 管理客户端

a) 创建一个后台管理的客户端,可以是web网站。也可以是别的。
初始化远程服务器上的全部Scheduler

        private static IScheduler scheduler = null;
        public static void InitRemoteScheduler()
        {
            try
            {
                NameValueCollection properties = new NameValueCollection();
                properties["quartz.scheduler.instanceName"] = "schedMaintenanceService";
                properties["quartz.scheduler.proxy"] = "true";
                properties["quartz.scheduler.proxy.address"] = string.Format("{0}://{1}:{2}/QuartzScheduler", scheme, server, port);
                ISchedulerFactory sf = new StdSchedulerFactory(properties);

                scheduler = sf.GetScheduler();
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog("初始化远程任务管理器失败" + ex.StackTrace);
            }
        }        

b) 增加,暂停,删除任务等操作

        /// 

        /// 暂停任务
        ///

 

        /// 
        public static void PauseJob(string JobKey)
        {
            try
            {
                JobKey jk = new JobKey(JobKey);
                if (scheduler.CheckExists(jk))
                {
                    //任务已经存在则暂停任务
                    scheduler.PauseJob(jk);
                    LogHelper.WriteLog(string.Format("任务“{0}”已经暂停", JobKey));
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

这里,只说了暂停的,只不过其他的都差不多。

3. 其他

另外,Quartz.NET提供了crystal-quartz  来实现远程管理。CrystalQuartz.Example.RemoteScheduler.Server 示例。感兴趣的朋友, 可以自己手动配置看看。

 

Quartz.NET总结(三)如何配置Quartz

  前两篇文章,已经介绍了Quartz.NET的使用和Cron表达式表达式的写法,今天说一说Quartz的配置,Quartz相关的配置有三个quartz.config、quartz_jobs.xml、log4net.config。其中quartz.config是基本的配置, quartz_jobs.xml是相关的job 任务配置文件,log4net.config则是日志记录的配置。

1.  quartz.config ,是Quartz.NET是基本的配置,主要是配置quartz_jobs.xml 的路径,及其他相关的参数。默认的quartz任务配置文件为quartz_jobs.xml,在quartz服务的根目录下,可以通过quartz.config中quartz.plugin.xml.fileNames = ~/quartz_jobs.xml进行设置。

# You can configure your scheduler in either  configuration section

# or in quartz properties file

# Configuration section has precedence

 
quartz.scheduler.instanceName = ServerScheduler


# configure thread pool info

quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz

quartz.threadPool.threadCount = 10

quartz.threadPool.threadPriority = Normal

 

# job initialization plugin handles our xml reading, without it defaults are used

quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz

quartz.plugin.xml.fileNames = ~/quartz_jobs.xml

 

# export this server to remoting context

quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz

quartz.scheduler.exporter.port = 555

quartz.scheduler.exporter.bindName = QuartzScheduler

quartz.scheduler.exporter.channelType = tcp

quartz.scheduler.exporter.channelName = httpQuartz

 

2. quartz_jobs.xml,是相关的job 任务配置文件。主要是有job和trigger两个组要的节点。







  

    true

  

  

    

    

      Job1

      JobGroup

      Quartz Job1

      Quartz.Net.Jobs.Job1,Quartz.Net.Jobs

      true

      false

    

    

    

      Job2

      JobGroup

      Quartz Job2

      Quartz.Net.Jobs.Job2,Quartz.Net.Jobs

      true

      false

    

    

    

      

        Job1Trigger

        JobTriggerGroup

        Job1

        JobGroup

        0/30 * * * * ?

      

    

    

    

      

        Job2Trigger1

        JobTriggerGroup

        Job2

        JobGroup

        0 * * * * ?

      

    

  

job 任务,这个节点是用来定义每个具体的任务的,多个任务请创建多个job节点即可。

 

1. name  任务名称,同一个group中多个job的name不能相同,如:Job1

2. group 任务所属分组,用于标识任务所属分组,如:JobGroup

3. job-type 任务的具体类型及所属程序集,实现了IJob接口的包含完整命名空间的类名,程序集名称,如: Quartz.Net.Jobs.Job1,Quartz.Net.Jobs

其余节点按照默认设置即可。

trigger 任务触发器,主要定义在什么时间,以何种方式触发任务(job),同一个job可以定义多个trigger ,各个trigger 各自独立的执行调度,每个trigger 中必须定义一种触发器类型(calendar-interval、simple、cron)。

calendar-interval使用比较少,一般都是simple和cron,这里就不做说明。

simple 简单任务的触发器,可以调度用于重复执行的任务

1. name 触发器名称,同一个分组中的名称必须不同

2. group 触发器组

3. job-name 要调度的任务名称,该job-name必须和对应job节点中的name完全相同 ,如:Job1

4. job-group 调度任务(job)所属分组,该值必须和job中的group完全相同,如:JobGroup

5. repeat-count  任务执行次数,如:-1表示无限次执行,10表示执行10次

6. repeat-interval 任务触发间隔,单位为毫秒,如:10000 每10秒执行一次

cron 复杂任务触发器,主要使用cron表达式定制任务调度,一般用的较多的都是cron触发器。关于cron表达式的写法,请参考前一篇博客《Quartz.NET总结(二)CronTrigger和Cron表达式》

1. name 触发器名称,同一个分组中的名称必须不同

2. group 触发器组

3. description 触发器描述

4. job-name 要调度的任务名称,注意该job-name必须和对应job节点中的name完全相同,如:Job1

5. job-group 调度任务(job)所属分组,该值必须和job中的group完全相同,如:JobGroup

6. cron-expression 任务执行的cron表达式,如:0/30 * * * * ? 每30秒执行一次

其余节点按照默认设置即可。

根据实际项目中的需要灵活配置quartz_jobs.xml,不需要再进行额外的修改其他后台代码,实现灵活的多任务调度,需要注意的是修改了quartz_jobs.xml文件后,quartz服务默认不会重新加载该文件,需要重启下服务才行。

3. log4net.config

(略),请参考其他关于log4net的配置。

Quartz.NET总结(二)CronTrigger和Cron表达式

  Quartz.NET的任务调度,主要就是依靠CronTrigger和Cron表达式。Cron是已经在UNIX存在了很长一段时间,它有着强大和可靠的调度能力。CronTrigger类也正是是基于Cron调度能力。

 

  CronTrigger使用Cron表达式。它能灵活的设置各种计划任务,比如每周一至周五,早上8点执行某项任务。然而虽然Cron表达式功能强大,灵活,但是也很混乱难懂。所以便在这里介绍Cron表达式相关的配置。希望大家帮助大家快速理解。

 

Cron格式

  Cron表达式被用来配置CronTrigger实例。Cron表达式是一个由6,7个域(子表达式)和空格组成的字符串。每个子表达式都描述了一个单独的日程细节
是否强制
允许值
允许特殊字符
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
, - * /
所以,比如一个cron表达式字符串"0 0 10 ? * MON",这表示“每周一的中午10:00”。

  上面的列表,可以清楚的看出,所有的域中的值都有特定的合法范围,每个域都可以单独配置。例如:Day of week(周中的天)必须有值(SUN—SAT)或是使用特殊字符,所以”0 0 10 ? * MON”,就可以改成为”MON-FRI”, “MON, WED, FRI”甚至是”MON-WED,SAT”。

 
特殊字符说明

'*' 表示域中“每个”。比如在"Minutes"域中的*表示每分钟。

 

'?' 用在day-of-month及day-of-week域中,表示“没有指定值”。这对于需要指定一个或者两个域的值而不需要对其他域进行设置来说相当有用。例如,我想在一个月的某一天(例如,第十),而不在乎具体是哪一天,我会把“10”放在day-of-month 域,然后“?”在day-of-week里。

 

'-' 指定范围,例如,“10-12”在Hours域,表示10点到12点。

 

',' 指定附加值,例如,“MON,WED,FRI”在day-of-week域中,表示“星期一,星期三和星期五”。

 

'/' 没有具体的值,用来用于指定值的增量,例如, 如果在Seconds域中,'0/15',它表示“从0开始,每隔15秒”。

 

'L' 只用在day-of-month及day-of-week中,这个字符是"last"的简写,但是在两个域中的意义不同。例如,在day-of-month域中的"L"表示本月的最后一天,即,一月的31日,非闰年的二月的28日。如果它用在day-of-week中,则表示"7"或者"SAT"。但是,这个字符跟在别的值后面,则表示"当月的最后的周XXX"。例如:"6L" 或者 "FRIL"都表示本月的最后一个周五。同时,也可以用来指定第某个月的最后一天的倒数第几天,如“L-3”表示某月最后一天的倒数第三天。注意:当使用'L'选项时,最重要的是不要指定列表或者值范围,否则会导致混乱。

 

'W' 用于day-of-week域中指定给定日(星期一星期五)最近的一天。例如:"15W",则表示“距离月中15号最近的工作日是周几”。

 

'#' 表示本月中的第几个周几。例如:day-of-week域中的"6#3" 或者 "FRI#3"表示“本月中第三个周五”。
相关实例

 

表达式
含义
0 0 12 * * ?
每天中午12点
0 15 10 ? * *
每天上午10点15
0 15 10 * * ?
每天上午10点15
0 15 10 * * ? *
每天上午10点15
0 * 14 * * ?
每天下午2点到2点59的每分钟
0 0/5 14 * * ?
每天下午2点到2点59的每5分钟
0 0/5 14,18 * * ?
每天下午2点到2点59和6点到6点55的每5分钟
0 0-5 14 * * ?
每天下午2点到2点05的每分钟
0 10,44 14 ? 3 WED
3月份每周三下午2点10和2点44
0 15 10 ? * MON-FRI
每周一到周五的上午10点15
0 15 10 15 * ?
每个月第15天的上午10点15
0 15 10 L * ?
每个月最后一天的上午10点15
0 15 10 L-2 * ?
每个月最后一天的倒数第二天的上午10点15
0 15 10 ? * 6L
每月的最后一个周五的上午10点15
0 15 10 ? * 6L 2002-2005
2002年到2005年的每月的最后一个周五的上午10点15
0 15 10 ? * 6#3
每月的第三个周五上午10点15
0 0 12 1/5 * ?
每月的第一天开始,每5天的中午12点
0 11 11 11 11 ?
每年的11月11日11点11分
注意:'?' and '*' 在 day-of-week和day-of-month的影响

 

参考链接:http://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/crontrigger.html

Quartz.NET总结(一)入门

前段时间,花了大量的时间,将原先的计划任务,切换到Quartz.NET来进行管理。原先的后台定时服务都是通过计划任务来实现的,但是随着业务增长,计划任务也越来越多,每个后台服务,都得创建一个计划任务。日常的维护和管理非常麻烦。

 

于是乎,一咬牙,决定引入Quartz.NET框架,统一都管理全部的后台定时服务。切换过程确实很麻烦。直到今天,才终于有时间整理总结Quartz.NET的相关内容。

 

Quartz.NET的优点和使用场景,这里不再多说,网上有很多说明,总的来说就是,Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等。 Quartz.NET允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联,配置灵活方便。

 

参考官方学习文档:http://www.quartz-scheduler.net/documentation/index.html

快速搭建一个Quartz,源代码下载

 

 第一步:新建解决方案和相关项目,并安装相关程序包,如下图所示:

Quartz依赖Common.Logging和Common.Logging.Log4Net,而且Log4Net也是比较熟悉的日志工具,因此我们实际使用中,也是log4net记录日志,另外定时作业一般都是在window服务中,我们也可用Topshelf来创建我们的window服务。

 

第二步:创建两个Job类Job1,Job2。实现IJob,在Execute方法里编写要处理的业务逻辑,系统就会按照Quartz的配置,定时处理。

  using System;

  using System.Threading;
  namespace Quartz.Net.Jobs
  {
    /// 

    /// 实现IJob接口
    ///

 

    public class Job1 : IJob
    {
      private static readonly Common.Logging.ILog logger = Common.Logging.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
      public void Execute(IJobExecutionContext context)
      {
        try
        {
          logger.Info("Job1 任务运行开始");

          for (int i = 0; i < 5; i++)
          {

            Thread.Sleep(100);
            logger.InfoFormat("Job1 正在运行{0}", i);
          }

          logger.Info("Job1任务运行结束");
        }
        catch (Exception ex)
        {
          logger.Error("Job1 运行异常", ex);
        }

      }
    }
  }

 

 第三步:配置quartz.config、quartz_jobs.xml

Quartz 实例的基础配置:quartz.config

  # You can configure your scheduler in either  configuration section

  # or in quartz properties file

  # Configuration section has precedence

  quartz.scheduler.instanceName = QuartzTest

  # configure thread pool info

  quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz

  quartz.threadPool.threadCount = 10

  quartz.threadPool.threadPriority = Normal

  # job initialization plugin handles our xml reading, without it defaults are used

  quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz

  quartz.plugin.xml.fileNames = ~/quartz_jobs.xml

  # export this server to remoting context

  #quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz

  #quartz.scheduler.exporter.port = 555

  #quartz.scheduler.exporter.bindName = QuartzScheduler

  #quartz.scheduler.exporter.channelType = tcp

  #quartz.scheduler.exporter.channelName = httpQuartz

 

各个Job 任务的配置 :quartz_jobs.xml


  
  "http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
  
    true
  
  
    
    
      Job1
      JobGroup
      Quartz Job1
      Quartz.Net.Jobs.Job1,Quartz.Net.Jobs
      true
      false
    
    
    
      Job2
      JobGroup
      Quartz Job2
      Quartz.Net.Jobs.Job2,Quartz.Net.Jobs
      true
      false
    
    
    
      
        Job1Trigger
        JobTriggerGroup
        Job1
        JobGroup
        0/30 * * * * ?
      
    
    
    
      
        Job2Trigger1
        JobTriggerGroup
        Job2
        JobGroup
        0 * * * * ?
      
    
  

 

第四步:宿主程序,可以是window服务,也可以是后台Console程序,如何用Topshelf来创建我们的window服务,请看另外一篇文章《使用Topshelf 开发windows服务》

 namespace Quartz.Net.Console
  {
    class Program
    {
      private static IScheduler scheduler;
      static void Main(string[] args)
      {
        ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
        scheduler = schedulerFactory.GetScheduler();

        scheduler.Start();
      }
    }
  }

 

注意:quartz_jobs.xml和quartz.config这两个文件,要手动复制到输出目录下,或者在vs中,分别选中这两个文件→右键属性→复制到输入目录设为:始终复制。

否则读取不到这两个配置文件,程序无法运行。

运行后,效果如下图:

 

使用Topshelf 开发windows服务

  在业务系统中,我们为了调度一些自动执行的任务或从队列中消费一些消息,所以基本上都会涉及到后台服务的开发。如果用windows service开发,非常不爽的一件事就是:调试相对麻烦,而且你还需要了解 windows service 相关的一些开发和安装部署,所以,windows service非常的不方便。Topshelf框架,能够让你将 console application 直接封装为 windows service,这样你在开发的时候,就可以直接在console 程序上开发,然后构成windows service。可以说非常方便。省去了很多不必要的麻烦。

 

  Topshelf是一个开源的跨平台的宿主服务框架,支持Windows和Mono,只需要几行代码就可以构建一个很方便使用的服务宿主。TopShelf 支持多个服务实例,使用的API非常简单,也提高了和 Log4Net的集成,结合Quartz.net,可以快速实现任务调度服务。

 

  1.下载地址

    https://github.com/Topshelf/Topshelf/downloads

 

  2.创建Console宿主程序TopshelfTest,并引用TopShelf.dll

 

  3.相关代码

public class TownCrier
{

    readonly Timer _timer;

    public TownCrier()
    {

        _timer = new Timer(1000) { AutoReset = true };

        _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now);

    }

    public void Start() { _timer.Start(); }

    public void Stop() { _timer.Stop(); }

}



// 入口代码

public class Program
{

    public static void Main()
    {

        HostFactory.Run(x =>                                             //1.我们用HostFactory.Run来设置一个宿主主机。我们初始化一个新的lambda表达式X,来显示这个宿主主机的全部配置。
        {

            x.Service(s =>                                   //2.告诉Topshelf ,有一个类型为“towncrier服务”,通过定义的lambda 表达式的方式,配置相关的参数。
            {

                s.ConstructUsing(name => new TownCrier());          //3.告诉Topshelf如何创建这个服务的实例,目前的方式是通过new 的方式,但是也可以通过Ioc 容器的方式:getInstance()。
  
                s.WhenStarted(tc => tc.Start());                       //4.开始 Topshelf 服务。

                s.WhenStopped(tc => tc.Stop());                       //5.停止 Topshelf 服务。

            });

            x.RunAsLocalSystem();                                         //6.这里使用RunAsLocalSystem() 的方式运行,也可以使用命令行(RunAsPrompt())等方式运行。

            x.SetDescription("Sample Topshelf Host");                  //7.设置towncrier服务在服务监控中的描述。

            x.SetDisplayName("Stuff");                                    //8.设置towncrier服务在服务监控中的显示名字。

            x.SetServiceName("Stuff");                                    //9.设置towncrier服务在服务监控中的服务名字。

        });

    }

}

  本人英文水平有限,代码的备注说明,随便翻译的,凑合看吧。

 

  4. 安装和卸载windows服务

                 TopshelfTest.exe install        #安装Windows服务

     TopshelfTest.exe uninstall     #卸载Windows服务

     TopshelfTest.exe install -instance “xxx” -servicename “xxx” -description “xxx” -displayname “xxx”    #安装不同的实例