Quartz .Net( 二) :可视化界面、xml配置、window服务自启动
可视化界面
首先新建一个web项目,其框架为.Net Framework4.5.2,然后需要添加引入Quartz和CrystalQuartz.Remote两个Nuget包。之后我们可以在项目的web.config中看到其为我们添加了以下的一些节点。其中添加的 handler 用来处理我们访问路径 CrystalQuartzPanel.axd时的处理程序,而provider中提供的端口8008则是用来做此web与quartz中的scheduler通信的端口。
<configSections> <sectionGroup name="crystalQuartz" type="CrystalQuartz.Web.Configuration.CrystalQuartzConfigurationGroup"> <section name="provider" type="CrystalQuartz.Web.Configuration.ProviderSectionHandler" requirePermission="false" allowDefinition="Everywhere" /> </sectionGroup> </configSections> <handlers> <add name="CrystalQuartzPanel" verb="*" path="CrystalQuartzPanel.axd" type="CrystalQuartz.Web.PagesHandler, CrystalQuartz.Web" /> </handlers> <crystalQuartz> <provider> <add property="Type" value="CrystalQuartz.Core.SchedulerProviders.RemoteSchedulerProvider, CrystalQuartz.Core" /> <!-- Edit scheduler host value below =================================== --> <add property="SchedulerHost" value="tcp://localhost:8008/QuartzScheduler" /> <!-- =================================== --> </provider> </crystalQuartz>
那么Quartz.Net中的Scheduler如何与其通信呢?在StdSchedulerFactory的构造函数中有一个带参构造函数,可以通过配置一些属性来实现。
public StdSchedulerFactory(NameValueCollection props)
具体属性设置如下
public class ScheduleManager { public async static Task<IScheduler> BuildScheduler() { var properties = new NameValueCollection(); properties["quartz.scheduler.instanceName"] = "后台作业监控系统"; // 设置线程池 properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz"; properties["quartz.threadPool.threadCount"] = "5"; properties["quartz.threadPool.threadPriority"] = "Normal"; // 远程输出配置 properties["quartz.scheduler.exporter.type"] = "Quartz.Simpl.RemotingSchedulerExporter, Quartz"; properties["quartz.scheduler.exporter.port"] = "8008"; properties["quartz.scheduler.exporter.bindName"] = "QuartzScheduler"; properties["quartz.scheduler.exporter.channelType"] = "tcp"; var schedulerFactory = new StdSchedulerFactory(properties); IScheduler _scheduler = await schedulerFactory.GetScheduler(); return _scheduler; } }
然后通过ScheduleManager.BuildScheduler()来创建IScheduler,就可以通过可视化界面来监控和管理我们的定时任务了。
在打开的网站后加上webconfig 黄色标记的那个Path值上去,回车,即可,效果如下图



xml配置
前面介绍的任务的创建执行是通过代码来实现的,当要添加一个任务的时候就非常的不灵活,做不到热插拔。而通过配置文件的方式实现配置化,可以做到在添加一个任务的话,我们可以新建一个类库来定义Job做到热插拔。
具体实现方法也很简单,创建一个配置文件quartz_job.config,其定义了 CheckPaymentStateJob 和 CheckPaymentStateTrigger1的一个配置。
XMLSchedulingDataProcessor processor = new XMLSchedulingDataProcessor(new SimpleTypeLoadHelper()); await processor.ProcessFileAndScheduleJobs("~/config/quartz_job.config", scheduler);
<?xml version="1.0" encoding="UTF-8"?> <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>CheckPaymentStateJob</name> <group>CheckStateGroup</group> <description>支付状态回调检查</description> <job-type>DispatcherProject.QuartzNet.CheckJob.CheckPaymentStateJob,DispatcherProject.QuartzNet</job-type> <durable>true</durable> <recover>false</recover> </job> <trigger> <cron> <name>CheckPaymentStateTrigger1</name> <group>CheckState</group> <job-name>CheckPaymentStateJob</job-name> <job-group>CheckStateGroup</job-group> <cron-expression>0 0/1 * * * ?</cron-expression> </cron> </trigger> </schedule> </job-scheduling-data>
而最后需要做的就是将该配置文件添加到Scheduler中就可以了。
XMLSchedulingDataProcessor processor = new XMLSchedulingDataProcessor(new SimpleTypeLoadHelper()); await processor.ProcessFileAndScheduleJobs("~/config/quartz_job.config", scheduler);
window服务自启动
之前的介绍都是基于Web的,这种实现任务调度的方式很少见,因为不管是MVC、WebApi还是WebService,它们都需要寄宿在IIS上运行,然而我们知道IIS是会定时回收进程池的,在一段时间内如果该站点没有收到任何请求,其就会进行回收,等到下次请求再自动启动。这样如果我们的任务需要在某个固定时间点执行就无法保障了。
因此,采用Windows Service来实现定时任务是更加合适的,它是由操作系统进行调度的,我们可以设置将服务设置为自启动,它将随着操作系统的启动而运行该进程。
新建一个Windows Service项目,其会为我们创建一个主入口
static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> static void Main() { try { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new Service1() }; ServiceBase.Run(ServicesToRun); } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } }
我们可以通过在Service1中来启动定时任务,通过log4net可以记录一些必要的日志
public partial class Service1 : ServiceBase { private Logger logger = new Logger(typeof(Service1)); public Service1() { InitializeComponent(); } protected override void OnStart(string[] args) { this.logger.Info("This is OnStart.."); IScheduler scheduler = await ScheduleManager.BuildScheduler(); //使用配置文件 XMLSchedulingDataProcessor processor = new XMLSchedulingDataProcessor(new SimpleTypeLoadHelper()); await processor.ProcessFileAndScheduleJobs("~/config/quartz_jobs.config", scheduler); } protected override void OnStop() { this.logger.Info("This is OnStop.."); } }
<?xml version="1.0" encoding="utf-8"?> <log4net> <!-- Define some output appenders --> <appender name="rollingAppender" type="log4net.Appender.RollingFileAppender"> <file value="log\log.txt" /> <!--追加日志内容--> <appendToFile value="true" /> <!--防止多线程时不能写Log,官方说线程非安全--> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <!--可以为:Once|Size|Date|Composite--> <!--Composite为Size和Date的组合--> <rollingStyle value="Composite" /> <!--当备份文件时,为文件名加的后缀--> <datePattern value="yyyyMMdd.TXT" /> <!--日志最大个数,都是最新的--> <!--rollingStyle节点为Size时,只能有value个日志--> <!--rollingStyle节点为Composite时,每天有value个日志--> <maxSizeRollBackups value="20" /> <!--可用的单位:KB|MB|GB--> <maximumFileSize value="3MB" /> <!--置为true,当前最新日志文件名永远为file节中的名字--> <staticLogFileName value="true" /> <!--输出级别在INFO和ERROR之间的日志--> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="INFO" /> <param name="LevelMax" value="FATAL" /> </filter> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/> </layout> </appender> <!-- levels: OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL --> <root> <priority value="ALL"/> <level value="ALL"/> <appender-ref ref="rollingAppender" /> </root> </log4net>
public class Logger { static Logger() { XmlConfigurator.Configure(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config\\log4net.config"))); ILog Log = LogManager.GetLogger(typeof(Logger)); Log.Info("系统初始化Logger模块"); } private ILog loger = null; public Logger(Type type) { loger = LogManager.GetLogger(type); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> /// <param name="ex"></param> public void Error(string msg = "出现异常", Exception ex = null) { loger.Error(msg, ex); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Warn(string msg) { loger.Warn(msg); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Info(string msg) { loger.Info(msg); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Debug(string msg) { loger.Debug(msg); } }
最后我们可以在Service1的界面上右键添加安装程序,来设置服务的一些信息。



接着通过编译完成后就可以通过下面的命令来安装或卸载服务了
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil QuartzProject.WindowsService.exe C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil /u QuartzProject.WindowsService.exe
服务改名称后,需要先原样卸载,再重新编译安装,要不然卸载还蛮麻烦的

付费内容,请联系本人QQ:1002453261
本文来自博客园,作者:明志德道,转载请注明原文链接:https://www.cnblogs.com/for-easy-fast/p/12470613.html

浙公网安备 33010602011771号