首页 > 代码库 > Topshelf结合Quartz.NET实现服务端定时调度任务
Topshelf结合Quartz.NET实现服务端定时调度任务
这周接受到一个新的需求:一天内分时间段定时轮询一个第三方WebAPI,并保存第三方WebAPI结果。
需求分析:分时段、定时开启、定时结束、轮询。主要工作集中在前三个上,轮询其实就是个Http请求,比较好解决。
技术选型:
1、最简单的方式:Windows Service、Timer、HttpClient。
2、B格高点的方式:Topshelf、Quartz.NET、HttpClient。
之所以选用第二种方式的原因:
1、Windows Service尝试写了一个,发现附加进程调试确实麻烦,而且以后若是需求变更,还需要重新调试发布Windows Service
2、Timer需要在项目中建立多个,区分起来着实麻烦
3、刚好在学习使用Quartz.NET,打算过段时间做个MVC版本的调度任务管理系统
4、经过查找cnblog发现,使用Topshelf可以用基于Console的模式先编写、调试程序,等调试通过后,用Topshelf命令即可完成Windows Service安装,据说还可以在Linux上通过Mono安装,也算是可以支持跨平台的咯(*^_^*)或许也可以通过制作Docker镜像来实现。
Show Code:
1、添加依赖Nuget包:Topshelf、Topshelf.Log4Net、Quartz、Common.Logging、Common.Logging.Core、Common.Logging.Log4Net1211、log4Net
2、创建ServiceRunner.cs类,继承ServiceControl, ServiceSuspend,这是为了用Topshelf的Start()、Stop()、Continue()、Pause()来分别执行Quartz任务调度的Start()、Shutdown()、ResumeAll()、PauseAll()方法
1 public class ServiceRunner : ServiceControl, ServiceSuspend 2 { 3 private readonly IScheduler scheduler; 4 public ServiceRunner() 5 { 6 scheduler = StdSchedulerFactory.GetDefaultScheduler(); 7 } 8 public bool Continue(HostControl hostControl) 9 {10 scheduler.ResumeAll();11 return true;12 }13 14 public bool Pause(HostControl hostControl)15 {16 scheduler.PauseAll();17 return true;18 }19 20 public bool Start(HostControl hostControl)21 {22 scheduler.Start();23 return true;24 }25 26 public bool Stop(HostControl hostControl)27 {28 scheduler.Shutdown(false);29 return true;30 }31 }
3、我在这里采用Topshelf的Custom Service模式,在Main()方法中写如下代码
1 HostFactory.Run(x => 2 { 3 x.UseLog4Net(); 4 x.Service<ServiceRunner>(); 5 x.SetDescription("QuartzDemo服务描述"); 6 x.SetDisplayName("QuartzDemo服务显示名称"); 7 x.SetServiceName("QuartzDemo服务名称"); 8 9 x.EnablePauseAndContinue();10 });
4、到此为止,建Windows Service的工作算是基本结束,接下来就是重点了,如何用Quartz做一个定时任务。但是这个过程并不难,这里我采用的是Quartz的Cron模式,相比较Simple模式,此种模式通过配置来制定Trigger触发和Job的执行,在我的Windows Service创建好后,无须我再次编译,只需要替换进一个实现IJob接口的动态链接库,并且在Quartz_jobs.xml配置即可,实现IJob的测试代码如下:
1 public class TestJob : IJob 2 { 3 private readonly ILog _log = LogManager.GetLogger(typeof(TestJob)); 4 public void Execute(IJobExecutionContext context) 5 { 6 7 _log.Info("测试Job,时间:"+ DateTime.Now.ToString("r")); 8 9 }10 }
5、准备Quartz.NET的配置文件quartz.config、quartz_jobs.xml,Quartz的Initialize()方法默认从编译输出目录下读取quartz.config文件,并且在quartz.config文件增加quartz.plugin.xml.fileNames 节点写 ~/quartz_jobs.xml,用来配置Trigger和Job的执行
1 # You can configure your scheduler in either <quartz> configuration section 2 # or in quartz properties file 3 # Configuration section has precedence 4 5 quartz.scheduler.instanceName = QuartzTest 6 7 # configure thread pool info 8 quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz 9 quartz.threadPool.threadCount = 1010 quartz.threadPool.threadPriority = Normal11 12 # job initialization plugin handles our xml reading, without it defaults are used13 quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz14 quartz.plugin.xml.fileNames = ~/quartz_jobs.xml15 16 # export this server to remoting context17 #quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz18 #quartz.scheduler.exporter.port = 55519 #quartz.scheduler.exporter.bindName = QuartzScheduler20 #quartz.scheduler.exporter.channelType = tcp21 #quartz.scheduler.exporter.channelName = httpQuartz
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <!-- This file contains job definitions in schema version 2.0 format --> 4 5 <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"> 6 7 <processing-directives> 8 <overwrite-existing-data>true</overwrite-existing-data> 9 </processing-directives>10 11 <schedule>12 13 <!--TestJob测试 任务配置-->14 <job>15 <name>TestJob</name>16 <group>Test</group>17 <description>TestJob测试</description>18 <job-type>WindowsService.TestJob,WindowsService</job-type>19 <durable>true</durable>20 <recover>false</recover>21 </job>22 <trigger>23 <cron>24 <name>TestJobTrigger</name>25 <group>Test</group>26 <job-name>TestJob</job-name>27 <job-group>Test</job-group>28 <!--<start-time>2017-08-03T16:00:00+16:00</start-time>29 <end-time>2017-08-03T18:10:00+18:10</end-time>-->30 <cron-expression>0/3 * 0-6 * * ?</cron-expression>31 </cron>32 </trigger>33 34 </schedule>35 </job-scheduling-data>
6、至此,我们已经基本可以把项目run起来了,但是这只能在console上看到每三秒打印一行“测试Job,时间:***”,并不能确定在以Windows Service时,定时任务调度也能按计划执行,我们还需要将日志输出到文件,需要继续配置log4Net
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> 5 </configSections> 6 7 <log4net> 8 <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> 9 <!--日志路径-->10 <param name= "File" value= "F:\App_Log\servicelog\"/>11 <!--是否是向文件中追加日志-->12 <param name= "AppendToFile" value= "true"/>13 <!--不加utf-8编码格式,中文字符将显示成乱码-->14 <param name="Encoding" value="utf-8" />15 <!--log保留天数-->16 <param name= "MaxSizeRollBackups" value= "10"/>17 <!--日志文件名是否是固定不变的-->18 <param name= "StaticLogFileName" value= "false"/>19 <!--日志文件名格式为:2008-08-31.log-->20 <param name= "DatePattern" value= "yyyy-MM-dd".read.log""/>21 <!--日志根据日期滚动-->22 <param name= "RollingStyle" value= "Date"/>23 <layout type="log4net.Layout.PatternLayout">24 <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n %loggername" />25 </layout>26 </appender>27 28 <!-- 控制台前台显示日志 -->29 <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">30 <mapping>31 <level value="ERROR" />32 <foreColor value="Red, HighIntensity" />33 </mapping>34 <mapping>35 <level value="Info" />36 <foreColor value="Green" />37 </mapping>38 <layout type="log4net.Layout.PatternLayout">39 <conversionPattern value="%n%date{HH:mm:ss,fff} [%-5level] %m" />40 </layout>41 42 <filter type="log4net.Filter.LevelRangeFilter">43 <param name="LevelMin" value="Info" />44 <param name="LevelMax" value="Fatal" />45 </filter>46 </appender>47 48 <root>49 <!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) -->50 <level value="all" />51 <appender-ref ref="ColoredConsoleAppender"/>52 <appender-ref ref="RollingLogFileAppender"/>53 </root>54 </log4net>55 </configuration>
同时在Main()方法中增加一行读取log4Net配置文件的代码,另:quartz.config、quartz_jobs.xml、log4net.config这三个文件,分别选中→右键属性→复制到输入目录设为:始终复制
1 static void Main(string[] args) 2 { 3 log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "log4net.config")); 4 HostFactory.Run(x => 5 { 6 x.UseLog4Net(); 7 x.Service<ServiceRunner>(); 8 x.SetDescription("QuartzDemo服务描述"); 9 x.SetDisplayName("QuartzDemo服务显示名称");10 x.SetServiceName("QuartzDemo服务名称");11 12 x.EnablePauseAndContinue();13 });14 }
最后,我们还需要将Topshelf注册到Windows中,在CMD中打开debug文件夹,Topshelf命令如下,
参考
Quartz.NET
官方学习文档:http://www.quartz-scheduler.net/documentation/index.html
使用实例介绍:http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html
官方的源代码下载:http://sourceforge.net/projects/quartznet/files/quartznet/
Topself文档:http://topshelf-project.com/
Log4Net文档:http://logging.apache.org/log4net/
Topshelf结合Quartz.NET实现服务端定时调度任务