MyException - 我的异常网
当前位置:我的异常网» C# » Topshelf组合Quartz.NET实现服务端定时调度任务

Topshelf组合Quartz.NET实现服务端定时调度任务

www.MyException.Cn  网友分享于:2013-08-09  浏览:0次
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     }
View Code

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             });
View Code

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     }
View Code

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 = 10
10 quartz.threadPool.threadPriority = Normal
11 
12 # job initialization plugin handles our xml reading, without it defaults are used
13 quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
14 quartz.plugin.xml.fileNames = ~/quartz_jobs.xml
15 
16 # export this server to remoting context
17 #quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
18 #quartz.scheduler.exporter.port = 555
19 #quartz.scheduler.exporter.bindName = QuartzScheduler
20 #quartz.scheduler.exporter.channelType = tcp
21 #quartz.scheduler.exporter.channelName = httpQuartz
quartz.config
 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>
quartz_jobs.xml

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&quot;.read.log&quot;"/>
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>
log4net.config

同时在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         }
View Code

最后,我们还需要将Topshelf注册到Windows中,在CMD中打开debug文件夹,Topshelf命令如下,

安装:WindowsService.exe install
启动:WindowsService.exe start
卸载:WindowsService.exe uninstall
至此,算是基本完成,我接下来只需要在一个继承IJob的类中实现业务代码即可。(*^_^*)

参考

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/

 

1楼裸奔货
6666

文章评论

当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
鲜为人知的编程真相
鲜为人知的编程真相
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
编程语言是女人
编程语言是女人
如何成为一名黑客
如何成为一名黑客
代码女神横空出世
代码女神横空出世
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
 程序员的样子
程序员的样子
旅行,写作,编程
旅行,写作,编程
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
总结2014中国互联网十大段子
总结2014中国互联网十大段子
老程序员的下场
老程序员的下场
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
一个程序员的时间管理
一个程序员的时间管理
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序员必看的十大电影
程序员必看的十大电影
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
那些争议最大的编程观点
那些争议最大的编程观点
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员和编码员之间的区别
程序员和编码员之间的区别
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
10个调试和排错的小建议
10个调试和排错的小建议
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
程序员的鄙视链
程序员的鄙视链
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
我的丈夫是个程序员
我的丈夫是个程序员
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
Java程序员必看电影
Java程序员必看电影
程序员应该关注的一些事儿
程序员应该关注的一些事儿
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
每天工作4小时的程序员
每天工作4小时的程序员
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
我是如何打败拖延症的
我是如何打败拖延症的
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有