震惊!Windows Service服务和定时任务框架quartz之间原来是这种关系……

过场CG:
 
接到公司领导的文件指示,“小熊”须要在6月底去海外执行一个行动代号为【定时任务】的营救计划,这个计划关系到公司某个项目的生死(数据安全漏洞),做战部拟定两个做战方案:
  方案一:使用务定时任务框架quartz;
  方案二:使用windows Service服务。
 
最终的做战方案为:二者配套使用。

 
前言:项目开发完成后,对接的项目有不少个模块,因为其中的一个环节疏忽,如今须要在原有的基础上把缺失的数据自动写入数据库存储起来。
从新修改程序逻辑已然不现实,如今须要一个补丁来进行逻辑更正。
补丁逻辑:两个入口控制,
  • 入口一:点击【更新】按钮同步逻辑后的数据;
  • 入口二:天天晚上18:00进行执行同步逻辑后的数据;

 
如今咱们先使用window服务进行入口二的编写(入口一只须要一个按钮调用入口二的逻辑便可)
windows服务

1、开发环境html

操做系统:Windows 7 X64/32数据库

开发环境:VS2017编程

编程语言:C#windows

.NET版本:.NET Framework 4.6.1安全

2、建立Windows Service微信

一、新建一个Windows Service,并将项目名称改成“MyWindowsService”,以下图所示:并发

 

 二、在解决方案资源管理器内将Service1.cs改成MyWindowsService.cs后并在左边页面空白处点击鼠标右键,添加安装程序,以下图所示:微信公众平台

 

添加安装程序:框架

 

 

三、 此时软件会生成两个组件,分别为“serviceInstaller1”及“serviceProcessInstaller1”,点击“serviceInstaller1”,右键--->属性,编程语言

将ServiceName改成MyWindowsService,Description改成“个人服务”,以下图:

 

 

 四、点击“serviceProcessInstaller1”,在“属性”窗体将Account改成LocalSystem(服务属性系统级别),以下图所示:

 
 
 五、点击MyWindowsService.cs,在左边空白位置右键,“查看代码”,而后编写代码,代码我编写好了,直接拷贝便可使用
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyWindowsService
{
    public partial class MyWindowsService : ServiceBase
    {
        public MyWindowsService()
        {
            InitializeComponent();
        }

        //建立进程
        public static Thread threadStartConfirmActualTime;  //建立一个时间进程
        public static Thread threadDoCheck;                 //检查日志时间进程

        //开启服务
        protected override void OnStart(string[] args)
        {
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t服务启动!\n");
            StartServer();
        }

        //中止服务
        protected override void OnStop()
        {
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t服务中止!\n");
        }

        //启动服务操做
        private void StartServer()
        {
            try
            {
                threadStartConfirmActualTime = new Thread(new ThreadStart(new SingleClass().BeginConfirmMessageTime));//在进程下面建立线程
                threadStartConfirmActualTime.Start();
                threadDoCheck.Start();
            }
            catch (Exception ex)
            {
                threadStartConfirmActualTime.Abort();//关闭线程
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t服务中止!"+ex.Message+"\n");
            }
        }


        /// <summary>
        /// Aouth:xiongze
        /// Time:2020/06/02
        /// Details:单例模式_创建一个单例类,保证只有一个对象被实例化,而后开启服务
        /// </summary>
        public class SingleClass  //单例模式_创建一个单例类,保证只有一个对象被实例化
        {
            public static SingleClass _SingleClass;
            public static object onlock = new object();  //实例化一个锁

            public static SingleClass Singleton
            {
                get
                {
                    if (_SingleClass == null)
                    {
                        lock (onlock)
                        {
                            _SingleClass = new SingleClass();
                        }
                    }
                    return _SingleClass;
                }
            }
            public void BeginConfirmMessageTime()  //开启服务
            {
                while (true)
                {
                    //天天晚上18这一个小时内检测执行
                    if (DateTime.Now.Hour.ToString("18") == "18")
                    {
                        try
                        {
                            //同步数据
                            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "我在"+ DateTime.Now + "同步了数据哦!\n");
                        }
                        catch (Exception ex)
                        {
                            //记录错误日志(记录到相应的文件下面)
                            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t我是错误日志!" + ex.Message + "\n");
                        }
                    }
                    Thread.Sleep(1800000);  //半个小时执行一次,注意,1000毫秒=1秒,具体须要多少时间能够自由换算 1800000半小时
                }

            }
        }

    }
}
 
六、至此,Windows服务已经建立完毕。
 
 3、建立安装、启动、中止、卸载服务的Windows窗体
 
一、点击项目,右键,从新生成
在桌面上建立一个文件夹,命名为“个人服务”,将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定时任务

 

1、开发环境

操做系统:Windows 7 X64

开发环境:VS2017

编程语言:C#

.NET版本:.NET Framework 4.6.1

2、建立quartz定时任务
一、建立一个控制台任务程序进行演示,命名为MyQuartz,建立以下:
 

 

 二、引入quartz框架动态连接库

在NuGet管理里面搜索quartz进行安装,注意: Quartz高版本的存在兼容性,建议使用低版本的(2.5.0)
以下图:
 

 

 

三、建立一个执行的类,用于执行后台数据逻辑,命名为TestJob,而且继承Quartz框架的IJob接口,这个累的内容以下,能够直接拷贝
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyQuartz
{
    public class TestJob: IJob
    {
        public void Execute(IJobExecutionContext context)//指定调用的方法
        {
            try
            {
                //在这里写代码(写本身的业务逻辑)
                Console.WriteLine("任务执行啦" + DateTime.Now);
            }
            catch (Exception ex)
            {
                Console.WriteLine("定时任务出错" + ex.Message);
            }
        }
    }
}

四、在Program.cs文件里面进行调用编写,编写内容主要以下:

  1. 建立一个做业调度池;
  2. 建立出来一个具体的做业;
  3. 建立并配置一个触发器;
  4. 加入做业调度池中;
  5. 开始运行。
 首先咱们看完成代码,而后进行讲解(代码能够直接拷贝):
using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyQuartz
{
    class Program
    {
        static void Main(string[] args)
        {
            //1.首先建立一个做业调度池
            ISchedulerFactory schedf = new StdSchedulerFactory();
            IScheduler sched = schedf.GetScheduler();
            //2.建立出来一个具体的做业
            IJobDetail job = JobBuilder.Create<TestJob>().Build();
            //3.建立并配置一个触发器

            #region(使用SimpleTrigger触发器,每次3秒执行一次,无上限)
            ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(3).WithRepeatCount(int.MaxValue)).Build();
            #endregion

            #region 每3秒执行一次 总共5次 ,开始执行时间设定在当前时间,结束时间我设定在2小时后,不过5次执行完没2小时候都再也不执行。
            //-------NextGivenSecondDate:若是第一个参数为null则表名当前时间日后推迟2秒的时间点。
            //DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddSeconds(5), 2);
            //DateTimeOffset endTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddHours(2), 3);
            //ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create().StartAt(startTime).EndAt(endTime)
            //                            .WithSimpleSchedule(x => x.WithIntervalInSeconds(3).WithRepeatCount(5))
            //                            .Build();
            #endregion

            #region (使用CronTrigger触发器)在每小时的第10,20,25,26,33,54分钟,每分钟的第1,10,14秒执行一次
            //DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddSeconds(1), 2);
            //DateTimeOffset endTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddYears(2), 3);
            //ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create().StartAt(startTime).EndAt(endTime)
            //                            .WithCronSchedule("1,10,59 10,20,21,26,33,54 * * * ? ")
            //                            .Build();
            #endregion
            //4.加入做业调度池中
            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.REPEAT_INDEFINITELY。重复的间隔,必须是0,或者long型的正数,表示毫秒。注意,若是重复间隔为0,trigger将会以重复次数并发执行(或者以scheduler能够处理的近似并发数)。

若是你还不熟悉DateBuilder,了解后你会发现使用它能够很是方便地构造基于开始时间(或终止时间)的调度策略。

endTime属性的值会覆盖设置重复次数的属性值;好比,你能够建立一个trigger,在终止时间以前每隔10秒执行一次,你不须要去计算在开始时间和终止时间之间的重复次数,只须要设置终止时间并将重复次数设置为REPEAT_INDEFINITELY(固然,你也能够将重复次数设置为一个很大的值,并保证该值比trigger在终止时间以前实际触发的次数要大便可)。

 

具体用法咱们就不水文了,你们去看Quartz官网文档的用法便可SimpleTrigger触发器使用规则:https://www.w3cschool.cn/quartz_doc/quartz_doc-67a52d1f.html,部分截图显示以下:
  • 指定时间开始触发,不重复:
  • 指定时间触发,每隔10秒执行一次,重复10次:
  • 5分钟之后开始触发,仅执行一次:
  • 当即触发,每一个5分钟执行一次,直到22:00:
  • 创建一个触发器,将在下一个小时的整点触发,而后每2小时重复一次:

 

 

CronTriggerr触发器(基于Cron表达式的触发器CronTriggerr

CronTrigger一般比Simple Trigger更有用,若是您须要基于日历的概念而不是按照SimpleTrigger的精确指定间隔进行从新启动的做业启动计划。

使用CronTrigger,您能够指定号时间表,例如“每周五中午”或“每一个工做日和上午9:30”,甚至“每周一至周五上午9:00至10点之间每5分钟”和1月份的星期五“。

即便如此,和SimpleTrigger同样,CronTrigger有一个startTime,它指定什么时候生效,以及一个(可选的)endTime,用于指定什么时候中止计划。

Cron Expressions
Cron-Expressions用于配置CronTrigger的实例。Cron Expressions是由七个子表达式组成的字符串,用于描述日程表的各个细节。这些子表达式用空格分隔,并表示:

Seconds
Minutes
Hours
Day-of-Month
Month
Day-of-Week
Year (optional field)
一个完整的Cron-Expressions的例子是字符串“0 0 12?* WED“ - 这意味着”每一个星期三下午12:00“。
单个子表达式能够包含范围和/或列表。例如,能够用“MON-FRI”,“MON,WED,FRI”或甚至“MON-WED,SAT”代替前一个(例如“WED”)示例中的星期几字段。
通配符(' '字符)可用于说明该字段的“每一个”可能的值。所以,前一个例子的“月”字段中的“”字符仅仅是“每月”。所以,“星期几”字段中的“*”显然意味着“每周的每一天”。
全部字段都有一组能够指定的有效值。这些值应该是至关明显的 - 例如秒和分钟的数字0到59,数小时的值0到23。日期能够是1-31的任何值,可是您须要注意在给定的月份中有多少天!月份能够指定为0到11之间的值,或者使用字符串JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV和DEC。星期几能够指定为1到7(1 =星期日)之间的值,或者使用字符串SUN,MON,TUE,WED,THU,FRI和SAT。
'/'字符可用于指定值的增量。例如,若是在“分钟”字段中输入“0/15”,则表示“每隔15分钟,从零开始”。若是您在“分钟”字段中使用“3/20”,则意味着“每隔20分钟,从三分钟开始” - 换句话说,它与“分钟”中的“3,243,43”相同领域。请注意“ / 35”的细微之处并不表明“每35分钟” - 这意味着“每隔35分钟,从零开始” - 或者换句话说,与指定“0,35”相同。
'' 字符是容许的日期和星期几字段。用于指定“无特定值”。当您须要在两个字段中的一个字段中指定某个字符而不是另外一个字段时,这颇有用。请参阅下面的示例(和CronTrigger JavaDoc)以进行说明。
“L”字符容许用于月日和星期几字段。这个角色对于“最后”来讲是短暂的,可是在这两个领域的每个领域都有不一样的含义。例如,“月”字段中的“L”表示“月的最后一天” - 1月31日,非闰年2月28日。若是在本周的某一天使用,它只是意味着“7”或“SAT”。可是若是在星期几的领域中再次使用这个值,就意味着“最后一个月的xxx日”,例如“6L”或“FRIL”都意味着“月的最后一个星期五”。您还能够指定从该月最后一天的偏移量,例如“L-3”,这意味着日历月份的第三个到最后一天。当使用'L'选项时,重要的是不要指定列表或值的范围,由于您会获得混乱/意外的结果。
“W”用于指定最近给定日期的工做日(星期一至星期五)。例如,若是要将“15W”指定为月日期字段的值,则意思是:“最近的平日到当月15日”。
''用于指定本月的“第n个”XXX工做日。例如,“星期几”字段中的“63”或“FRI#3”的值表示“本月的第三个星期五”。
如下是一些表达式及其含义的更多示例 - 您能够在JavaDoc中找到更多的org.quartz.CronExpression

Cron Expressions示例
CronTrigger示例1 - 建立一个触发器的表达式,每5分钟就会触发一次
“0 0/5 * * *?”

CronTrigger示例2 - 建立触发器的表达式,每5分钟触发一次,分钟后10秒(即上午10时10分,上午10:05:10等)。
“10 0/5 * * *?”

CronTrigger示例3 - 在每一个星期三和星期五的10:3011:3012:30和13:30建立触发器的表达式。
“0 30 10-13?* WED,FRI“

CronTrigger示例4 - 建立触发器的表达式,每月5日和20日上午8点至10点之间每半小时触发一次。请注意,触发器将不会在上午10点开始,仅在8:008:309:00和9:300 0/30 8-9 5,20 *?”

请注意,一些调度要求太复杂,没法用单一触发表示 - 例如“每上午9:00至10:00之间每5分钟,下午1:00至晚上10点之间每20分钟”一次。在这种状况下的解决方案是简单地建立两个触发器,并注册它们来运行相同的做业。

 

具体使用方法见CronTrigger触发器使用规则https://www.w3cschool.cn/quartz_doc/quartz_doc-lwuv2d2a.html

  • 创建一个触发器,每隔一分钟,天天上午8点至下午5点之间:
  • 创建一个触发器,将在上午10:42天天发射:
  • 创建一个触发器,将在星期三上午10:42在TimeZone(系统默认值)以外触发:

 

 

 


执行演示

写完后咱们查看执行结果,我使用的是SimpleTrigger触发器,每3秒执行一次,无上限,各位能够根据自身的项目需求更改使用不一样的触发器

 

 

注意:

若是定时任务框架quartz这个挂在iis上会被回收掉(默认是20分钟)

Quartz高版本的存在兼容性,建议使用低版本的(2.5.0)

 


 

总结

到这里Windows Service服务和定时任务框架quartz都简单的介绍完了,具体使用哪个或者配套使用就看自己项目逻辑了;

如今执行的逻辑:

Windows Service服务:程序随电脑开机启动,每隔半个小时执行一次,检测到执行时间等于我设置的时间就去执行后台逻辑;

定时任务框架quartz:若是发布在iis上,默认20分钟后会被回收(程序不能一直等待执行),程序处于休眠状态,到指定时候后唤醒(触发器)程序执行后台逻辑;

 

PS:若是把quartz结合windows服务使用的话就不存在被回收问题;

 
欢迎关注订阅个人微信公众平台【熊泽有话说】,更多好玩易学知识等你来取
做者:熊泽-学习中的苦与乐
公众号:熊泽有话说
出处: https://www.cnblogs.com/xiongze520/p/13031944.html
创做不易,任何人或团体、机构所有转载或者部分转载、摘录,请在文章明显位置注明做者和原文连接。  
相关文章
相关标签/搜索