Quartz.NET Windows 服务示例
想必大家在项目中处理简单的后台持续任务或者定时触发任务的时候均使用 Thread 或者 Task 来完成,但是项目中的这种需求一旦多了的话就得将任务调度引入进来了,那今天就简单的介绍一下 Quartz.NET 基于 Windows 服务宿主是怎样配置使用的。
Quartz.NET 是一个优秀的任务调度框架,移植于 Java 版的 Quartz 。
示例环境
- .Net 4.5.2
- Quartz 2.4.1
- Common.Logging 3.3.1
- log4net 2.0.5
- Common.Logging.Log4Net1213 3.3.1
源码地址:https://github.com/Wlitsoft/QuartzNETWinServiceSample
配置
1. quartz.config
这个配置文件需要放在服务运行根目录下,用于指定 quartz 的一些运行配置,比如调度名称、线程池实现组件、线程池大小、任务配置文件路径等。
# You can configure your scheduler in either <quartz> configuration section
# or in quartz properties file
# Configuration section has precedence quartz.scheduler.instanceName = QuartzNETWinServiceScheduler # 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 = ~/Conf/jobs.config
暂时需求需要修改的只有一处,看最后一行 quartz.plugin.xml.fileNames = ~/Conf/jobs.config 指定任务的配置文件路径。
2. 任务配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file contains job definitions in schema version 2.0 format -->
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"> <processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives> <schedule>
<job>
<name>Job1</name>
<group>Jobs</group>
<description>任务1</description>
<job-type>Wlitsoft.ProjectSample.QuartzNETWinService.Job.Job1,QuartzNETWinService</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<simple>
<name>Job1Trigger</name>
<group>Jobs</group>
<description>每 30 秒执行一次</description>
<job-name>Job1</job-name>
<job-group>Jobs</job-group>
<repeat-count>-1</repeat-count>
<repeat-interval>30000</repeat-interval>
</simple>
</trigger>
</schedule>
</job-scheduling-data>
以下为配置文件属性:
- 任务 (job 节点)
| job 节点项说明 | ||||
| 名称 | 类型 | 是否必填 | 默认值 | 描述 |
| name | string | Y | 任务名称 | |
| group | string | N | 默认组名 | 任务组名称 |
| description | string | N | 任务描述 | |
| job-type | string | Y | 任务的类型全名称,一般实现 IJob 接口 | |
| durable | boolean | N | false | 任务完成后是否依然保存到数据库 |
| recover | boolean | N | false | 应用或服务重启之后是否忽略过期任务 |
- 触发器 (trigger 节点)
下面说下最常用的两种触发器:
1)简单触发器(simple 节点)用于触发定时轮训执行的任务。
| simple 节点项说明 | ||||
| 名称 | 类型 | 是否必填 | 默认值 | 描述 |
| name | string | Y | 触发器名称 | |
| group | string | N | 默认组名 | 触发器名称 |
| description | string | N | 触发器描述 | |
| job-name | string | Y | 要触发的任务的名称 | |
| job-group | string | Y | 要触发的任务的组名称 | |
| repeat-count | int | Y | 0 | 重复次数(0:不执行;-1:不限次数) |
| repeat-interval | long | Y | 0 | 间隔时间(单位:毫秒) |
| start-time | date | N | 当前时间 | 开始时间 |
| end-time | date | N | 结束时间(如果不指定则一直执行直到重复次数) | |
2)Cron 触发器(cron 节点)根据 cron 表达式触发任务。
| cron 节点项说明 | ||||
| 名称 | 类型 | 是否必填 | 默认值 | 描述 |
| name | string | Y | 触发器名称 | |
| group | string | N | 默认组名 | 触发器名称 |
| description | string | N | 触发器描述 | |
| job-name | string | Y | 要触发的任务的名称 | |
| job-group | string | Y | 要触发的任务的组名称 | |
| cron | string | Y | 规则表达式 | |
| start-time | date | N | 当前时间 | 开始时间 |
| end-time | date | N | 结束时间 | |
注:cron 表达式在线生成:http://cron.qqe2.com
3. 日志配置文件
1) app.config
- configSctions
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
- commong.logging 配置
<common>
<logging>
<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net1213">
<arg key="configType" value="FILE-WATCH" />
<arg key="configFile" value="~/Conf/log4net.config" />
<arg key="level" value="INFO" />
</factoryAdapter>
</logging>
</common>
- configType : 用于指定日子配置文件类型,取值:INLINE - 在当前配置文件总;FILE-WATCH - 配置文件中。
- configFile:配置文件路径。
- level:日子输出级别。
- log4net 配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net> <logger name="job1" additivity="false" >
<level value="ALL" />
<appender-ref ref="Job1TxtAppender" />
</logger> <appender name="Job1TxtAppender" type="log4net.Appender.RollingFileAppender">
<file value="log/job1.txt" />
<appendToFile value="true" />
<param name="MaxSizeRollBackups" value="10"/>
<param name="MaximumFileSize" value="10240KB"/>
<param name="RollingStyle" value="Size"/>
<param name="StaticLogFileName" value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender> <appender name="InfoFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log/" />
<appendToFile value="true" />
<param name="DatePattern" value="yyyy-MM-dd'.txt'" />
<rollingStyle value="Date" />
<maxSizeRollBackups value="100" />
<maximumFileSize value="1024KB" />
<staticLogFileName value="false" />
<Encoding value="UTF-8" />
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="INFO" />
<param name="LevelMax" value="INFO" />
</filter>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level %logger - %message%newline" />
</layout>
</appender>
<appender name="ErrorFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log/error.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="100" />
<maximumFileSize value="10240KB" />
<staticLogFileName value="true" />
<Encoding value="UTF-8" />
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="WARN" />
<param name="LevelMax" value="FATAL" />
</filter>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="InfoFileAppender" />
<appender-ref ref="ErrorFileAppender" />
</root>
</log4net> </configuration>
log4net.config
主程序代码:
using System.ServiceProcess;
using Common.Logging;
using Quartz;
using Quartz.Impl; namespace Wlitsoft.ProjectSample.QuartzNETWinService
{
public partial class MainService : ServiceBase
{
#region 私有属性 //日志记录这。
private readonly ILog _logger; //调度器。
private readonly IScheduler _scheduler; #endregion #region 构造方法 /// <summary>
/// 初始化 <see cref="MainService"/> 类的新实例。
/// </summary>
public MainService()
{
InitializeComponent();
this._logger = LogManager.GetLogger(this.GetType());
StdSchedulerFactory factory = new StdSchedulerFactory();
this._scheduler = factory.GetScheduler();
} #endregion protected override void OnStart(string[] args)
{
this._scheduler.Start();
this._logger.Info("服务启动");
} protected override void OnStop()
{
if (!this._scheduler.IsShutdown)
this._scheduler.Shutdown();
this._logger.Info("服务停止");
} protected override void OnPause()
{
this._scheduler.PauseAll();
base.OnPause();
} protected override void OnContinue()
{
this._scheduler.ResumeAll();
base.OnContinue();
}
}
}
示例任务代码:
using Common.Logging;
using Quartz; namespace Wlitsoft.ProjectSample.QuartzNETWinService.Job
{
public class Job1 : IJob
{
//日志构造者。
private static readonly ILog Logger = LogManager.GetLogger("job1"); #region Implementation of IJob /// <summary>
/// Called by the <see cref="T:Quartz.IScheduler" /> when a <see cref="T:Quartz.ITrigger" />
/// fires that is associated with the <see cref="T:Quartz.IJob" />.
/// </summary>
/// <remarks>
/// The implementation may wish to set a result object on the
/// JobExecutionContext before this method exits. The result itself
/// is meaningless to Quartz, but may be informative to
/// <see cref="T:Quartz.IJobListener" />s or
/// <see cref="T:Quartz.ITriggerListener" />s that are watching the job's
/// execution.
/// </remarks>
/// <param name="context">The execution context.</param>
public void Execute(IJobExecutionContext context)
{
string jobDes = context.JobDetail.Description;
Logger.Info($"{jobDes}运行");
} #endregion
}
}
源码地址:https://github.com/Wlitsoft/QuartzNETWinServiceSample
推荐阅读:一个技术汪的开源梦
Quartz.NET Windows 服务示例的更多相关文章
- Quartz.NET Windows
Quartz.NET Windows 服务示例 想必大家在项目中处理简单的后台持续任务或者定时触发任务的时候均使用 Thread 或者 Task 来完成,但是项目中的这种需求一旦多了的话就得将任务调度 ...
- Quartz和TopShelf Windows服务作业调度
上一次写了一遍关于Quartz作业调度的文章 Quartz.NET 作业调度使用 现在使用TopShelf和Quartz实现windows服务作业调度 TopShelf版本4.0 Quartz版本3. ...
- C#开发windows服务如何调试——资料整理
原文标题:C# Windows服务程序如何进行调试 原文地址:https://jingyan.baidu.com/article/456c463b18e1b00a583144b3.html 第一种: ...
- 使用普通Windows服务创建Quartz.Net服务项目
Quartz.NET 项目地址 http://quartznet.sourceforge.net/ 源码下载地址:Quartz.Net.2.0 首先创建Quartz.Net.2.0解决方案,添加 Wi ...
- Quartz+TopShelf实现Windows服务作业调度
Quartz:首先我贴出来了两段代码(下方),可以看出,首先会根据配置文件(quartz.config),包装出一个Quartz.Core.QuartzScheduler instance,这是一个调 ...
- 使用 Topshelf 组件一步一步创建 Windows 服务 (2) 使用Quartz.net 调度
上一篇说了如何使用 Topshelf 组件快速创建Windows服务,接下来介绍如何使用 Quartz.net 关于Quartz.net的好处,网上搜索都是一大把一大把的,我就不再多介绍. 先介绍需要 ...
- Windows服务 + Quartz.NET
服务基础 安装管理员打开cmd cd C:\Windows\Microsoft.NET\Framework\v4.0.30319 InstallUtil.exe Path_WinSvc.exe 或者 ...
- Quartz.Net在windows服务中的使用
写在前面 这几天在弄一个项目,需要定时抓取一些数据,当时也想直接用timer算了.因为之前也弄过这样的项目,但是一想,已经用过了,再去使用同一种思路,未免太乏味了.就换了一种新玩法.这里将之前看到的一 ...
- Windows服务调用Quartz.net 实现消息调度
Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...
随机推荐
- jquery和Js的区别和基础操作
jqery的语法和js的语法一样,算是把js升级了一下,这两种语法可以一起使用,只不过是用jqery更加方便 一个页面想要使用jqery的话,先要引入一下jqery包,jqery包从网上下一个就可以, ...
- WebAPi之SelfHost自创建证书启动Https疑难解惑及无法正确返回结果
前言 话说又来需求了,之前对于在SelfHost中需要嵌套页面并操作为非正常需求,这回来正常需求了,客户端现在加了https,老大过来说WebAPi访问不了了,这是什么情况,我去试了试,还真是这个情况 ...
- 解决vs创建或打开C++浏览数据库文件*.sdf时发生错误的问题
VS2012, 创建或打开C++浏览数据库文件*.sdf时发生错误. IntelliSense 和浏览信息将不能用于C++项目. 请确保已安装 Microsoft SQL Server Compac ...
- ASP.NET加密和解密数据库连接字符串
大家知道,在应用程序中进行数据库操作需要连接字符串,而如果没有连接字符串,我们就无法在应用程序中完成检索数据,创建数据等一系列的数据库操作.当有人想要获取你程序中的数据库信息,他首先看到的可能会是We ...
- Asp.Net Core + Dapper + Repository 模式 + TDD 学习笔记
0x00 前言 之前一直使用的是 EF ,做了一个简单的小项目后发现 EF 的表现并不是很好,就比如联表查询,因为现在的 EF Core 也没有啥好用的分析工具,所以也不知道该怎么写 Linq 生成出 ...
- mysql5.x升级至mysql5.7后导入之前数据库date出错的解决方法!
mysql5.x升级至mysql5.7后导入之前数据库date出错的解决方法! 修改mysql5.7的配置文件即可解决,方法如下: linux版:找到mysql的安装路径进入默认的为/usr/shar ...
- [Xamarin] 透過Native Code呼叫 JavaScript function (转帖)
今天我們來聊聊關於如何使用WebView 中的Javascript 來呼叫 Native Code 的部分 首先,你得先來看看這篇[Xamarin] 使用Webview 來做APP因為這篇文章至少講解 ...
- iOS开发系列--数据存取
概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库.例如前面IOS开发系列-Objective-C之Foundation框架的文章中提到归档.plist文件存储, ...
- useful commands for docker beginner
You may want to add my wechat public account or add my technical blog's RSS feed This list is meant ...
- .NET Web开发技术简单整理
在最初学习一些编程语言.一些编程技术的时候,做的更多的是如何使用该技术,如何更好的使用该技术解决问题,而没有去关注它的相关性.关注它的理论支持,这种学习技术的方式是短平快.其实工作中有时候也是这样,公 ...