0%

Quartz+TopShelf实现Windows服务作业调度 - Frozen.Zhang - 博客园

Excerpt

Quartz:首先我贴出来了两段代码(下方),可以看出,首先根据配置文件(quartz.config),包装出一个Quartz.Core.QuartzScheduler
instance,这是一个调度器,调度各个任务项(Jobs)的执行。这个调度器可以被Start、被Shutdown、被Pause


  Quartz:首先我贴出来了两段代码(下方),可以看出,首先会根据配置文件(quartz.config),包装出一个Quartz.Core.QuartzScheduler

instance,这是一个调度器,调度各个任务项(Jobs)的执行。这个调度器可以被Start、被Shutdown、被PauseAll、被ResumeAll,这对应

了windows服务的开启、停止、暂停、恢复。当启动服务,我就调用调度器的Start(),停止服务我就调用调度器的Shutdown()方法。

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<span>namespace</span><span> QTDemo
{
</span><span>public</span> <span>class</span><span> ServiceRunner : ServiceControl, ServiceSuspend
{
</span><span>private</span> <span>readonly</span><span> IScheduler scheduler;

</span><span>public</span><span> ServiceRunner()
{
scheduler </span>=<span> StdSchedulerFactory.GetDefaultScheduler();
}

</span><span>public</span> <span>bool</span><span> Start(HostControl hostControl)
{
scheduler.Start();
</span><span>return</span> <span>true</span><span>;
}</span>

复制代码

复制代码

1
2
3
4
5
6
7
8
9
10
<span>//</span><span> 摘要: 
</span><span>//</span><span> An implementation of Quartz.ISchedulerFactory that does all of it's work
</span><span>//</span><span> of creating a Quartz.Core.QuartzScheduler instance based on the contents
</span><span>//</span><span> of a properties file.</span>
<span>public</span> <span>class</span><span> StdSchedulerFactory : ISchedulerFactory
{
</span><span>public</span> <span>const</span> <span>string</span> AutoGenerateInstanceId = <span>"</span><span>AUTO</span><span>"</span><span>;
</span><span>public</span> <span>const</span> <span>string</span> ConfigurationSectionName = <span>"</span><span>quartz</span><span>"</span><span>;
</span><span>public</span> <span>const</span> <span>string</span> DefaultInstanceId = <span>"</span><span>NON_CLUSTERED</span><span>"</span><span>;
</span><span>public</span> <span>const</span> <span>string</span> PropertiesFile = <span>"</span><span>quartz.config</span><span>"</span>;

复制代码

  那TopShelf又扮演了什么样的角色呢

  这是一个宿主服务的框架。和ServiceBase、ServiceInstaller那一套的目的一样,都是用来创建Windows服务的。

项目示例

1、新建项目控制台应用程序‘QTDemo’

2、从NuGet安装‘Quartz’,‘Topshelf’,‘Topshelf.Log4Net’ 

复制代码

1
2
3
4
5
6
7
8
9
<span>1</span> &lt;?xml version=<span>"</span><span>1.0</span><span>"</span> encoding=<span>"</span><span>utf-8</span><span>"</span>?&gt;
<span>2</span> &lt;packages&gt;
<span>3</span> &lt;package id=<span>"</span><span>Common.Logging</span><span>"</span> version=<span>"</span><span>3.3.1</span><span>"</span> targetFramework=<span>"</span><span>net45</span><span>"</span> /&gt;
<span>4</span> &lt;package id=<span>"</span><span>Common.Logging.Core</span><span>"</span> version=<span>"</span><span>3.3.1</span><span>"</span> targetFramework=<span>"</span><span>net45</span><span>"</span> /&gt;
<span>5</span> &lt;package id=<span>"</span><span>log4net</span><span>"</span> version=<span>"</span><span>2.0.5</span><span>"</span> targetFramework=<span>"</span><span>net45</span><span>"</span> /&gt;
<span>6</span> &lt;package id=<span>"</span><span>Quartz</span><span>"</span> version=<span>"</span><span>2.3.3</span><span>"</span> targetFramework=<span>"</span><span>net45</span><span>"</span> /&gt;
<span>7</span> &lt;package id=<span>"</span><span>Topshelf</span><span>"</span> version=<span>"</span><span>3.3.1</span><span>"</span> targetFramework=<span>"</span><span>net45</span><span>"</span> /&gt;
<span>8</span> &lt;package id=<span>"</span><span>Topshelf.Log4Net</span><span>"</span> version=<span>"</span><span>3.3.1</span><span>"</span> targetFramework=<span>"</span><span>net45</span><span>"</span> /&gt;
<span>9</span> &lt;/packages&gt;

复制代码

3、定义一个Job(一个任务项),继承Quartz.IJob

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<span> 1</span> <span>using</span><span> Quartz;
</span><span> 2</span> <span>namespace</span><span> QTDemo.QuartzJobs
</span><span> 3</span> <span>{
</span><span> 4</span> <span>public</span> <span>sealed</span> <span>class</span><span> TestJob : IJob
</span><span> 5</span> <span> {
</span><span> 6</span> <span>public</span> <span>void</span><span> Execute(IJobExecutionContext context)
</span><span> 7</span> <span> {
</span><span> 8</span> CommonHelper.AppLogger.InfoFormat(<span>"</span><span>TestJob测试</span><span>"</span><span>);
</span><span> 9</span>
<span>10</span> <span> }
</span><span>11</span> <span> }
</span><span>12</span> <span>}
</span><span>13</span>

复制代码

4、配置(新建)quartz.config、quartz_jobs.xml

quartz.config可直接使用,不用修改

View Code

quartz_jobs.xml根据实际的Job项修改

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<span> 1</span> <span>&lt;?</span><span>xml version="1.0" encoding="utf-8" </span><span>?&gt;</span>
<span> 2</span> <span>&lt;!--</span><span> This file contains job definitions in schema version 2.0 format </span><span>--&gt;</span>
<span> 3</span>
<span> 4</span> <span>&lt;</span><span>job-scheduling-data </span><span>xmlns</span><span>="http://quartznet.sourceforge.net/JobSchedulingData"</span><span> xmlns:xsi</span><span>="http://www.w3.org/2001/XMLSchema-instance"</span><span> version</span><span>="2.0"</span><span>&gt;</span>
<span> 5</span>
<span> 6</span> <span>&lt;</span><span>processing-directives</span><span>&gt;</span>
<span> 7</span> <span>&lt;</span><span>overwrite-existing-data</span><span>&gt;</span>true<span>&lt;/</span><span>overwrite-existing-data</span><span>&gt;</span>
<span> 8</span> <span>&lt;/</span><span>processing-directives</span><span>&gt;</span>
<span> 9</span>
<span>10</span> <span>&lt;</span><span>schedule</span><span>&gt;</span>
<span>11</span>
<span>12</span> <span>&lt;!--</span><span>TestJob测试 任务配置 </span><span>--&gt;</span>
<span>13</span> <span>&lt;</span><span>job</span><span>&gt;</span>
<span>14</span> <span>&lt;</span><span>name</span><span>&gt;</span>TestJob<span>&lt;/</span><span>name</span><span>&gt;</span>
<span>15</span> <span>&lt;</span><span>group</span><span>&gt;</span>Test<span>&lt;/</span><span>group</span><span>&gt;</span>
<span>16</span> <span>&lt;</span><span>description</span><span>&gt;</span>TestJob测试<span>&lt;/</span><span>description</span><span>&gt;</span>
<span>17</span> <span>&lt;</span><span>job-type</span><span>&gt;</span>QTDemo.QuartzJobs.TestJob,QTDemo<span>&lt;/</span><span>job-type</span><span>&gt;</span>
<span>18</span> <span>&lt;</span><span>durable</span><span>&gt;</span>true<span>&lt;/</span><span>durable</span><span>&gt;</span>
<span>19</span> <span>&lt;</span><span>recover</span><span>&gt;</span>false<span>&lt;/</span><span>recover</span><span>&gt;</span>
<span>20</span> <span>&lt;/</span><span>job</span><span>&gt;</span>
<span>21</span> <span>&lt;</span><span>trigger</span><span>&gt;</span>
<span>22</span> <span>&lt;</span><span>cron</span><span>&gt;</span>
<span>23</span> <span>&lt;</span><span>name</span><span>&gt;</span>TestJobTrigger<span>&lt;/</span><span>name</span><span>&gt;</span>
<span>24</span> <span>&lt;</span><span>group</span><span>&gt;</span>Test<span>&lt;/</span><span>group</span><span>&gt;</span>
<span>25</span> <span>&lt;</span><span>job-name</span><span>&gt;</span>TestJob<span>&lt;/</span><span>job-name</span><span>&gt;</span>
<span>26</span> <span>&lt;</span><span>job-group</span><span>&gt;</span>Test<span>&lt;/</span><span>job-group</span><span>&gt;</span>
<span>27</span> <span>&lt;!--</span><span> 从start-time起,每5s执行一次IJob.Execute </span><span>--&gt;</span>
<span>28</span> <span>&lt;</span><span>start-time</span><span>&gt;</span>2012-01-22T00:00:00+08:00<span>&lt;/</span><span>start-time</span><span>&gt;</span>
<span>29</span> <span>&lt;</span><span>cron-expression</span><span>&gt;</span>0/5 * * * * ?<span>&lt;/</span><span>cron-expression</span><span>&gt;</span>
<span>30</span> <span>&lt;/</span><span>cron</span><span>&gt;</span>
<span>31</span> <span>&lt;/</span><span>trigger</span><span>&gt;</span>
<span>32</span>
<span>33</span> <span>&lt;/</span><span>schedule</span><span>&gt;</span>
<span>34</span> <span>&lt;/</span><span>job-scheduling-data</span><span>&gt;</span>

复制代码

5、创建服务

入口:

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<span> 1</span> <span>using</span><span> System;
</span><span> 2</span> <span>using</span><span> System.IO;
</span><span> 3</span> <span>using</span><span> Topshelf;
</span><span> 4</span>
<span> 5</span> <span>namespace</span><span> QTDemo
</span><span> 6</span> <span>{
</span><span> 7</span> <span>class</span><span> Program
</span><span> 8</span> <span> {
</span><span> 9</span> <span>static</span> <span>void</span> Main(<span>string</span><span>[] args)
</span><span>10</span> <span> {
</span><span>11</span> log4net.Config.XmlConfigurator.ConfigureAndWatch(<span>new</span> FileInfo(AppDomain.CurrentDomain.BaseDirectory + <span>"</span><span>log4net.config</span><span>"</span><span>));
</span><span>12</span>
<span>16</span> HostFactory.Run(x =&gt;
<span>17</span> <span> {
</span><span>18</span> <span> x.UseLog4Net();
</span><span>19</span>
<span>20</span> x.Service&lt;ServiceRunner&gt;<span>();
</span><span>21</span>
<span>22</span> <span> x.RunAsLocalSystem();
</span><span>23</span>
<span>24</span> x.SetDescription(<span>"</span><span>Quartz+TopShelf实现Windows服务作业调度的一个示例Demo</span><span>"</span><span>);
</span><span>25</span> x.SetDisplayName(<span>"</span><span>QuartzTopShelfDemo服务</span><span>"</span><span>);
</span><span>26</span> x.SetServiceName(<span>"</span><span>QuartzTopShelfDemoService</span><span>"</span><span>);
</span><span>27</span>
<span>28</span> <span> x.EnablePauseAndContinue();
</span><span>29</span>
<span>30</span> <span> });
</span><span>31</span> <span> }
</span><span>32</span> <span> }
</span><span>33</span> }

复制代码

ServiceRunner.cs

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<span> 1</span> <span>using</span><span> Quartz;
</span><span> 2</span> <span>using</span><span> Quartz.Impl;
</span><span> 3</span> <span>using</span><span> Topshelf;
</span><span> 4</span>
<span> 5</span> <span>namespace</span><span> QTDemo
</span><span> 6</span> <span>{
</span><span> 7</span> <span>public</span> <span>class</span><span> ServiceRunner : ServiceControl, ServiceSuspend
</span><span> 8</span> <span> {
</span><span> 9</span> <span>private</span> <span>readonly</span><span> IScheduler scheduler;
</span><span>10</span>
<span>11</span> <span>public</span><span> ServiceRunner()
</span><span>12</span> <span> {
</span><span>13</span> scheduler =<span> StdSchedulerFactory.GetDefaultScheduler();
</span><span>14</span> <span> }
</span><span>15</span>
<span>16</span> <span>public</span> <span>bool</span><span> Start(HostControl hostControl)
</span><span>17</span> <span> {
</span><span>18</span> <span> scheduler.Start();
</span><span>19</span> <span>return</span> <span>true</span><span>;
</span><span>20</span> <span> }
</span><span>21</span>
<span>22</span> <span>public</span> <span>bool</span><span> Stop(HostControl hostControl)
</span><span>23</span> <span> {
</span><span>24</span> scheduler.Shutdown(<span>false</span><span>);
</span><span>25</span> <span>return</span> <span>true</span><span>;
</span><span>26</span> <span> }
</span><span>27</span>
<span>28</span> <span>public</span> <span>bool</span><span> Continue(HostControl hostControl)
</span><span>29</span> <span> {
</span><span>30</span> <span> scheduler.ResumeAll();
</span><span>31</span> <span>return</span> <span>true</span><span>;
</span><span>32</span> <span> }
</span><span>33</span>
<span>34</span> <span>public</span> <span>bool</span><span> Pause(HostControl hostControl)
</span><span>35</span> <span> {
</span><span>36</span> <span> scheduler.PauseAll();
</span><span>37</span> <span>return</span> <span>true</span><span>;
</span><span>38</span> <span> }
</span><span>39</span>
<span>40</span> <span> }
</span><span>41</span> }

复制代码

log4net配置文件,可直接使用

View Code 

6、quartz.config、quartz_jobs.xml、log4net.config都设置成‘始终复制’

7、直接F5,调试,发现TestJob每5s执行一次

8、安装成Windows服务

用Release编一个版本

然后用命令行安装服务

服务面板里可以找到此服务

启动之,查看日志文件,服务运行正常

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<span>23</span>:<span>23</span>:<span>52</span>,<span>171</span> INFO -<span>Configuration Result:
[Success] Name QuartzTopShelfDemoService
[Success] DisplayName QuartzTopShelfDemo服务
[Success] Description Quartz</span>+<span>TopShelf实现Windows服务作业调度的一个示例Demo
[Success] ServiceName QuartzTopShelfDemoService
</span><span>23</span>:<span>23</span>:<span>52</span>,<span>185</span> INFO -Topshelf v3.<span>3.154</span>.<span>0</span>, .NET Framework v4.<span>0.30319</span>.<span>34014</span>
<span>23</span>:<span>23</span>:<span>52</span>,<span>194</span> ERROR-The QuartzTopShelfDemoService service can only be installed <span>as</span><span> an administrator
</span><span>23</span>:<span>25</span>:<span>54</span>,<span>758</span> INFO -<span>Configuration Result:
[Success] Name QuartzTopShelfDemoService
[Success] DisplayName QuartzTopShelfDemo服务
[Success] Description Quartz</span>+<span>TopShelf实现Windows服务作业调度的一个示例Demo
[Success] ServiceName QuartzTopShelfDemoService
</span><span>23</span>:<span>25</span>:<span>54</span>,<span>772</span> INFO -Topshelf v3.<span>3.154</span>.<span>0</span>, .NET Framework v4.<span>0.30319</span>.<span>34014</span>
<span>23</span>:<span>25</span>:<span>54</span>,<span>781</span> DEBUG-Attempting to install <span>'</span><span>QuartzTopShelfDemoService</span><span>'</span>
<span>23</span>:<span>25</span>:<span>54</span>,<span>901</span> INFO -<span>Installing QuartzTopShelfDemo服务 service
</span><span>23</span>:<span>25</span>:<span>55</span>,<span>123</span> DEBUG-<span>Opening Registry
</span><span>23</span>:<span>25</span>:<span>55</span>,<span>123</span> DEBUG-Service path: <span>"</span><span>E:\DotNetProject\Y2016\QTDemo\QTDemo\bin\Release\QTDemo.exe</span><span>"</span>
<span>23</span>:<span>25</span>:<span>55</span>,<span>123</span> DEBUG-Image path: <span>"</span><span>E:\DotNetProject\Y2016\QTDemo\QTDemo\bin\Release\QTDemo.exe</span><span>"</span> -displayname <span>"</span><span>QuartzTopShelfDemo服务</span><span>"</span> -servicename <span>"</span><span>QuartzTopShelfDemoService</span><span>"</span>
<span>23</span>:<span>25</span>:<span>58</span>,<span>357</span> DEBUG-<span>Closing Registry
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>442</span> INFO -<span>Configuration Result:
[Success] Name QuartzTopShelfDemoService
[Success] DisplayName QuartzTopShelfDemo服务
[Success] Description Quartz</span>+<span>TopShelf实现Windows服务作业调度的一个示例Demo
[Success] ServiceName QuartzTopShelfDemoService
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>455</span> INFO -Topshelf v3.<span>3.154</span>.<span>0</span>, .NET Framework v4.<span>0.30319</span>.<span>34014</span>
<span>23</span>:<span>28</span>:<span>10</span>,<span>649</span> DEBUG-<span>Started by the Windows services process
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>649</span> DEBUG-Running <span>as</span><span> a service, creating service host.
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>651</span> INFO -Starting <span>as</span><span> a Windows service
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>654</span> DEBUG-[Topshelf] Starting up <span>as</span><span> a windows service application
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>657</span> INFO -<span>[Topshelf] Starting
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>658</span> DEBUG-<span>[Topshelf] Current Directory: E:\DotNetProject\Y2016\QTDemo\QTDemo\bin\Release
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>658</span> DEBUG-<span>[Topshelf] Arguments:
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>940</span> INFO -<span>[Topshelf] Started
</span><span>23</span>:<span>28</span>:<span>10</span>,<span>988</span> INFO -<span>TestJob测试
</span><span>23</span>:<span>28</span>:<span>15</span>,<span>000</span> INFO -<span>TestJob测试
</span><span>23</span>:<span>28</span>:<span>20</span>,<span>000</span> INFO -TestJob测试

复制代码

  附:源码下载