WindowsService服务和定时任务框架quartz之
前言:项目开发完成后,对接的项目有很多个模块,由于其中的一个环节疏忽,现在需要在原有的基础上把缺失的数据自动写入数据库存储起来。
重新修改程序逻辑已然不现实,现在需要一个补丁来进行逻辑更正。
补丁逻辑:两个入口控制,入口一:点击【更新】按钮同步逻辑后的数据;入口二:每天晚上18:00进行执行同步逻辑后的数据;
现在我们先使用window服务进行入口二的编写(入口一只需要一个按钮调用入口二的逻辑即可)
windows服务一、开发环境
操作系统:Windows7X6432
开发环境:VS2017
编程语言:C
。NET版本:。NETFramework4。6。1
二、创建WindowsService
1、新建一个WindowsService,并将项目名称改为MyWindowsService,如下图所示:
2、在解决方案资源管理器内将Service1。cs改为MyWindowsService。cs后并在左边页面空白处点击鼠标右键,添加安装程序,如下图所示:
添加安装程序:
3、此时软件会生成两个组件,分别为serviceInstaller1及serviceProcessInstaller1,点击serviceInstaller1,右键属性,
将ServiceName改为MyWindowsService,Description改为我的服务,如下图:
4、点击serviceProcessInstaller1,在属性窗体将Account改为LocalSystem(服务属性系统级别),如下图所示:
5、点击MyWindowsService。cs,在左边空白位置右键,查看代码,然后编写代码,代码我编写好了,直接拷贝即可使用
usingSystem;usingSystem。Collections。Generic;usingSystem。ComponentModel;usingSystem。Data;usingSystem。Diagnostics;usingSystem。Linq;usingSystem。ServiceProcess;usingSystem。Text;usingSystem。Threading;usingSystem。Threading。Tasks;namespaceMyWindowsService{publicpartialclassMyWindowsService:ServiceBase{publicMyWindowsService(){InitializeComponent();}创建进程publicstaticThreadthreadStartConfirmActualTime;创建一个时间进程publicstaticThreadthreadDoCheck;检查日志时间进程开启服务protectedoverridevoidOnStart(string〔〕args){Console。WriteLine(DateTime。Now。ToString(yyyyMMddHH:mm:ss)服务启动!);StartServer();}停止服务protectedoverridevoidOnStop(){Console。WriteLine(DateTime。Now。ToString(yyyyMMddHH:mm:ss)服务停止!);}启动服务操作privatevoidStartServer(){try{threadStartConfirmActualTimenewThread(newThreadStart(newSingleClass()。BeginConfirmMessageTime));在进程下面创建线程threadStartConfirmActualTime。Start();threadDoCheck。Start();}catch(Exceptionex){threadStartConfirmActualTime。Abort();关闭线程Console。WriteLine(DateTime。Now。ToString(yyyyMMddHH:mm:ss)服务停止!ex。Message);}}summaryAouth:xiongzeTime:20200602Details:单例模式建立一个单例类,保证只有一个对象被实例化,然后开启服务summarypublicclassSingleClass单例模式建立一个单例类,保证只有一个对象被实例化{publicstaticSingleClassSingleClass;publicstaticobjectonlocknewobject();实例化一个锁publicstaticSingleClassSingleton{get{if(SingleClassnull){lock(onlock){SingleClassnewSingleClass();}}returnSingleClass;}}publicvoidBeginConfirmMessageTime()开启服务{while(true){每天晚上18这一个小时内检测执行if(DateTime。Now。Hour。ToString(18)18){try{同步数据Console。WriteLine(DateTime。Now。ToString(yyyyMMddHH:mm:ss)我在DateTime。Now同步了数据哦!);}catch(Exceptionex){记录错误日志(记录到相应的文件下面)Console。WriteLine(DateTime。Now。ToString(yyyyMMddHH:mm:ss)我是错误日志!ex。Message);}}Thread。Sleep(1800000);半个小时执行一次,注意,1000毫秒1秒,具体需要多少时间可以自由换算1800000半小时}}}}}
6、至此,Windows服务已经创建完毕。
三、创建安装、启动、停止、卸载服务的Windows窗体
1、点击项目,右键,重新生成
在桌面上创建一个文件夹,命名为我的服务,将MyWindowsService项目项目生成的bin文件夹Debug文件夹的内容全部复制到新建的文件夹里面;
然后去百度拷贝三个文件到我的服务文件里面,分别为Install。bat(安装)、UnInstall。bat(卸载)、InstallUtil。exe(执行),
打开文件,分别打开Install。bat和UnInstall。bat文件,将后面一个xxx。exe修改为你的文件程序,我们的是MyWindowsService。exe。如下图
操作完后双击Install。bat进行安装windows服务,安装成功后点击【计算机】右键管理服务里面找到我的服务,启动服务并修改为自动启动;如下图:
这样就实现了windows服务入口二每天晚上18:00进行执行同步逻辑后的数据,只要代码不报错就一直执行;
优点:每天指定时间自动执行指定逻辑
缺点:程序在每次设置的时间内无限执行,消耗资源(CPU等)
quartz定时任务一、开发环境
操作系统:Windows7X64
开发环境:VS2017
编程语言:C
。NET版本:。NETFramework4。6。1
二、创建quartz定时任务
1、创建一个控制台任务程序进行演示,命名为MyQuartz,创建如下:
2、引入quartz框架动态链接库
在NuGet管理里面搜索quartz进行安装,注意:Quartz高版本的存在兼容性,建议使用低版本的(2。5。0)
如下图:
3、创建一个执行的类,用于执行后台数据逻辑,命名为TestJob,并且继承Quartz框架的IJob接口,这个累的内容如下,可以直接拷贝usingQuartz;usingSystem;usingSystem。Collections。Generic;usingSystem。Linq;usingSystem。Text;usingSystem。Threading。Tasks;namespaceMyQuartz{publicclassTestJob:IJob{publicvoidExecute(IJobExecutionContextcontext)指定调用的方法{try{在这里写代码(写自己的业务逻辑)Console。WriteLine(任务执行啦DateTime。Now);}catch(Exceptionex){Console。WriteLine(定时任务出错ex。Message);}}}}
4、在Program。cs文件里面进行调用编写,编写内容主要如下:创建一个作业调度池;创建出来一个具体的作业;创建并配置一个触发器;加入作业调度池中;开始运行。
首先我们看完成代码,然后进行讲解(代码可以直接拷贝):usingQuartz;usingQuartz。Impl;usingSystem;usingSystem。Collections。Generic;usingSystem。Linq;usingSystem。Text;usingSystem。Threading。Tasks;namespaceMyQuartz{classProgram{staticvoidMain(string〔〕args){1。首先创建一个作业调度池ISchedulerFactoryschedfnewStdSchedulerFactory();ISchedulerschedschedf。GetScheduler();2。创建出来一个具体的作业IJobDetailjobJobBuilder。CreateTestJob()。Build();3。创建并配置一个触发器region(使用SimpleTrigger触发器,每次3秒执行一次,无上限)ISimpleTriggertrigger(ISimpleTrigger)TriggerBuilder。Create()。WithSimpleSchedule(xx。WithIntervalInSeconds(3)。WithRepeatCount(int。MaxValue))。Build();endregionregion每3秒执行一次总共5次,开始执行时间设定在当前时间,结束时间我设定在2小时后,不过5次执行完没2小时候都不再执行。NextGivenSecondDate:如果第一个参数为null则表名当前时间往后推迟2秒的时间点。DateTimeOffsetstartTimeDateBuilder。NextGivenSecondDate(DateTime。Now。AddSeconds(5),2);DateTimeOffsetendTimeDateBuilder。NextGivenSecondDate(DateTime。Now。AddHours(2),3);ISimpleTriggertrigger(ISimpleTrigger)TriggerBuilder。Create()。StartAt(startTime)。EndAt(endTime)。WithSimpleSchedule(xx。WithIntervalInSeconds(3)。WithRepeatCount(5))。Build();endregionregion(使用CronTrigger触发器)在每小时的第10,20,25,26,33,54分钟,每分钟的第1,10,14秒执行一次DateTimeOffsetstartTimeDateBuilder。NextGivenSecondDate(DateTime。Now。AddSeconds(1),2);DateTimeOffsetendTimeDateBuilder。NextGivenSecondDate(DateTime。Now。AddYears(2),3);ICronTriggertrigger(ICronTrigger)TriggerBuilder。Create()。StartAt(startTime)。EndAt(endTime)。WithCronSchedule(1,10,5910,20,21,26,33,54?)。Build();endregion4。加入作业调度池中sched。ScheduleJob(job,trigger);5。开始运行sched。Start();Console。ReadKey();}}}
在上面代码中可以看出,我们主要使用了两个触发器:SimpleTrigger触发器和CronTrigger触发器;
SimpleTrigger触发器(简单触发器SimpleTrigger)
SimpleTrigger可以满足的调度需求是:在具体的时间点执行一次,或者在具体的时间点执行,并且以指定的间隔重复执行若干次。比如,你有一个trigger,你可以设置它在2015年1月13日的上午11:23:54准时触发,或者在这个时间点触发,并且每隔2秒触发一次,一共重复5次。
根据描述,你可能已经发现了,SimpleTrigger的属性包括:开始时间、结束时间、重复次数以及重复的间隔。这些属性的含义与你所期望的是一致的,只是关于结束时间有一些地方需要注意。
重复次数,可以是0、正整数,以及常量SimpleTrigger。REPEATINDEFINITELY。重复的间隔,必须是0,或者long型的正数,表示毫秒。注意,如果重复间隔为0,trigger将会以重复次数并发执行(或者以scheduler可以处理的近似并发数)。
如果你还不熟悉DateBuilder,了解后你会发现使用它可以非常方便地构造基于开始时间(或终止时间)的调度策略。
endTime属性的值会覆盖设置重复次数的属性值;比如,你可以创建一个trigger,在终止时间之前每隔10秒执行一次,你不需要去计算在开始时间和终止时间之间的重复次数,只需要设置终止时间并将重复次数设置为REPEATINDEFINITELY(当然,你也可以将重复次数设置为一个很大的值,并保证该值比trigger在终止时间之前实际触发的次数要大即可)。
具体用法我们就不水文了,大家去看Quartz官网文档的用法即可SimpleTrigger触发器使用规则:https:www。w3cschool。cnquartzdocquartzdoc67a52d1f。html,部分截图显示如下:指定时间开始触发,不重复:指定时间触发,每隔10秒执行一次,重复10次:5分钟以后开始触发,仅执行一次:立即触发,每个5分钟执行一次,直到22:00:建立一个触发器,将在下一个小时的整点触发,然后每2小时重复一次:
CronTriggerr触发器(基于Cron表达式的触发器CronTriggerr)
CronTrigger通常比SimpleTrigger更有用,如果您需要基于日历的概念而不是按照SimpleTrigger的精确指定间隔进行重新启动的作业启动计划。
使用CronTrigger,您可以指定号时间表,例如每周五中午或每个工作日和上午9:30,甚至每周一至周五上午9:00至10点之间每5分钟和1月份的星期五。
即使如此,和SimpleTrigger一样,CronTrigger有一个startTime,它指定何时生效,以及一个(可选的)endTime,用于指定何时停止计划。CronExpressionsCronExpressions用于配置CronTrigger的实例。CronExpressions是由七个子表达式组成的字符串,用于描述日程表的各个细节。这些子表达式用空格分隔,并表示:SecondsMinutesHoursDayofMonthMonthDayofWeekYear(optionalfield)一个完整的CronExpressions的例子是字符串0012?WED这意味着每个星期三下午12:00。单个子表达式可以包含范围和或列表。例如,可以用MONFRI,MON,WED,FRI或甚至MONWED,SAT代替前一个(例如WED)示例中的星期几字段。通配符(字符)可用于说明该字段的每个可能的值。因此,前一个例子的月字段中的字符仅仅是每个月。因此,星期几字段中的显然意味着每周的每一天。所有字段都有一组可以指定的有效值。这些值应该是相当明显的例如秒和分钟的数字0到59,数小时的值0到23。日期可以是131的任何值,但是您需要注意在给定的月份中有多少天!月份可以指定为0到11之间的值,或者使用字符串JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV和DEC。星期几可以指定为1到7(1星期日)之间的值,或者使用字符串SUN,MON,TUE,WED,THU,FRI和SAT。字符可用于指定值的增量。例如,如果在分钟字段中输入015,则表示每隔15分钟,从零开始。如果您在分钟字段中使用320,则意味着每隔20分钟,从三分钟开始换句话说,它与分钟中的3,243,43相同领域。请注意35的细微之处并不代表每35分钟这意味着每隔35分钟,从零开始或者换句话说,与指定0,35相同。?字符是允许的日期和星期几字段。用于指定无特定值。当您需要在两个字段中的一个字段中指定某个字符而不是另一个字段时,这很有用。请参阅下面的示例(和CronTriggerJavaDoc)以进行说明。L字符允许用于月日和星期几字段。这个角色对于最后来说是短暂的,但是在这两个领域的每一个领域都有不同的含义。例如,月字段中的L表示月的最后一天1月31日,非闰年2月28日。如果在本周的某一天使用,它只是意味着7或SAT。但是如果在星期几的领域中再次使用这个值,就意味着最后一个月的xxx日,例如6L或FRIL都意味着月的最后一个星期五。您还可以指定从该月最后一天的偏移量,例如L3,这意味着日历月份的第三个到最后一天。当使用L选项时,重要的是不要指定列表或值的范围,因为您会得到混乱意外的结果。W用于指定最近给定日期的工作日(星期一至星期五)。例如,如果要将15W指定为月日期字段的值,则意思是:最近的平日到当月15日。用于指定本月的第n个XXX工作日。例如,星期几字段中的63或FRI3的值表示本月的第三个星期五。以下是一些表达式及其含义的更多示例您可以在JavaDoc中找到更多的org。quartz。CronExpressionCronExpressions示例CronTrigger示例1创建一个触发器的表达式,每5分钟就会触发一次005?CronTrigger示例2创建触发器的表达式,每5分钟触发一次,分钟后10秒(即上午10时10分,上午10:05:10等)。1005?CronTrigger示例3在每个星期三和星期五的10:30,11:30,12:30和13:30创建触发器的表达式。0301013?WED,FRICronTrigger示例4创建触发器的表达式,每个月5日和20日上午8点至10点之间每半小时触发一次。请注意,触发器将不会在上午10点开始,仅在8:00,8:30,9:00和9:300030895,20?请注意,一些调度要求太复杂,无法用单一触发表示例如每上午9:00至10:00之间每5分钟,下午1:00至晚上10点之间每20分钟一次。在这种情况下的解决方案是简单地创建两个触发器,并注册它们来运行相同的作业。
具体使用方法见CronTrigger触发器使用规则:https:www。w3cschool。cnquartzdocquartzdoclwuv2d2a。html建立一个触发器,每隔一分钟,每天上午8点至下午5点之间:建立一个触发器,将在上午10:42每天发射:建立一个触发器,将在星期三上午10:42在TimeZone(系统默认值)之外触发:
执行演示
写完后我们查看执行结果,我使用的是SimpleTrigger触发器,每3秒执行一次,无上限,各位可以根据自身的项目需求更改使用不同的触发器
总结
到这里WindowsService服务和定时任务框架quartz都简单的介绍完了,具体使用哪一个或者配套使用就看本身项目逻辑了;
现在执行的逻辑:
WindowsService服务:程序随电脑开机启动,每隔半个小时执行一次,检测到执行时间等于我设置的时间就去执行后台逻辑;
定时任务框架quartz:如果发布在iis上,默认20分钟后会被回收(程序不能一直等待执行),程序处于休眠状态,到指定时候后唤醒(触发器)程序执行后台逻辑;
PS:如果把quartz结合windows服务使用的话就不存在被回收问题;