1. 程式人生 > >Topshelf結合Quartz.NET實現服務端定時調度任務

Topshelf結合Quartz.NET實現服務端定時調度任務

param eve basedir 定時 鏈接庫 uninstall for 技術 編寫

這周接受到一個新的需求:一天內分時間段定時輪詢一個第三方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/

Topshelf結合Quartz.NET實現服務端定時調度任務