震惊!Windows Service服务和定时任务框架quartz之间原来是这种关系……
过场CG:
接到公司领导的文件指示,“小熊”需要在6月底去海外执行一个行动代号为【定时任务】的营救计划,这个计划关系到公司某个项目的生死(数据安全漏洞),作战部拟定两个作战方案:
方案一:使用务定时任务框架quartz;
方案二:使用windows Service服务。
最终的作战方案为:两者配套使用。
|
- 入口一:点击【更新】按钮同步逻辑后的数据;
- 入口二:每天晚上18:00进行执行同步逻辑后的数据;
一、开发环境
操作系统:Windows 7 X64/32
开发环境:VS2017
编程语言:C#
.NET版本:.NET Framework 4.6.1
二、创建Windows Service
1、新建一个Windows Service,并将项目名称改为“MyWindowsService”,如下图所示:
2、在解决方案资源管理器内将Service1.cs改为MyWindowsService.cs后并在左边页面空白处点击鼠标右键,添加安装程序,如下图所示:
添加安装程序:
3、 此时软件会生成两个组件,分别为“serviceInstaller1”及“serviceProcessInstaller1”,点击“serviceInstaller1”,右键--->属性,
将ServiceName改为MyWindowsService,Description改为“我的服务”,如下图:
4、点击“serviceProcessInstaller1”,在“属性”窗体将Account改为LocalSystem(服务属性系统级别),如下图所示:
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("") == "")
{
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(); //半个小时执行一次,注意,1000毫秒=1秒,具体需要多少时间可以自由换算 1800000半小时
} }
} }
}
一、开发环境
操作系统:Windows 7 X64
开发环境:VS2017
编程语言:C#
.NET版本:.NET Framework 4.6.1
2、引入quartz框架动态链接库
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);
}
}
}
}
4、在Program.cs文件里面进行调用编写,编写内容主要如下:
- 创建一个作业调度池;
- 创建出来一个具体的作业;
- 创建并配置一个触发器;
- 加入作业调度池中;
- 开始运行。
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().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的一个具体实现,它可以触发一个已经安排进调度程序(任务执行计划)的任务,并可以指定时间间隔重复执行该任务。
SimpleTrigger 包含几个特点:开始时间、结束时间、重复次数以及重复执行的时间间隔。
重复的次数可以是零,一个正整数,或常量SimpleTrigger.REPEAT_INDEFINITELY
。
重复执行的时间间隔可以是零,或者long类型的数值表示毫秒。值得注意的是,零重复间隔会造成触发器同时发生(或接近同时作为调度管理)。
“结束时间”的属性会重写重复的次数,这可能是有用的,如果你想创建一个触发器,如每10秒触发一次,直到一个给定的时刻,而不是要计算的次数,它会在开始时间和结束时间重复执行。结束时间一到,就算你指定了重复次数很多次(比如执行10W次),但是时间一到它将不再执行。
SimpleTrigger 实例创建依赖于TriggerBuilder 和SimpleScheduleBuilder ,使用Quartz提供的DSL风格创建触发器实例,
- 指定时间开始触发,不重复:
- 指定时间触发,每隔10秒执行一次,重复10次:
- 5分钟以后开始触发,仅执行一次:
- 立即触发,每个5分钟执行一次,直到22:00:
- 建立一个触发器,将在下一个小时的整点触发,然后每2小时重复一次:
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的例子是字符串“ ?* WED“ - 这意味着”每个星期三下午12:“。
单个子表达式可以包含范围和/或列表。例如,可以用“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( =星期日)之间的值,或者使用字符串SUN,MON,TUE,WED,THU,FRI和SAT。
'/'字符可用于指定值的增量。例如,如果在“分钟”字段中输入“/”,则表示“每隔15分钟,从零开始”。如果您在“分钟”字段中使用“/”,则意味着“每隔20分钟,从三分钟开始” - 换句话说,它与“分钟”中的“,,”相同领域。请注意“ / ”的细微之处并不代表“每35分钟” - 这意味着“每隔35分钟,从零开始” - 或者换句话说,与指定“,”相同。
'?' 字符是允许的日期和星期几字段。用于指定“无特定值”。当您需要在两个字段中的一个字段中指定某个字符而不是另一个字段时,这很有用。请参阅下面的示例(和CronTrigger JavaDoc)以进行说明。
“L”字符允许用于月日和星期几字段。这个角色对于“最后”来说是短暂的,但是在这两个领域的每一个领域都有不同的含义。例如,“月”字段中的“L”表示“月的最后一天” - 1月31日,非闰年2月28日。如果在本周的某一天使用,它只是意味着“”或“SAT”。但是如果在星期几的领域中再次使用这个值,就意味着“最后一个月的xxx日”,例如“6L”或“FRIL”都意味着“月的最后一个星期五”。您还可以指定从该月最后一天的偏移量,例如“L-”,这意味着日历月份的第三个到最后一天。当使用'L'选项时,重要的是不要指定列表或值的范围,因为您会得到混乱/意外的结果。
“W”用于指定最近给定日期的工作日(星期一至星期五)。例如,如果要将“15W”指定为月日期字段的值,则意思是:“最近的平日到当月15日”。
'#'用于指定本月的“第n个”XXX工作日。例如,“星期几”字段中的“#”或“FRI#”的值表示“本月的第三个星期五”。
以下是一些表达式及其含义的更多示例 - 您可以在JavaDoc中找到更多的org.quartz.CronExpression Cron Expressions示例
CronTrigger示例1 - 创建一个触发器的表达式,每5分钟就会触发一次
“ / * * *?” CronTrigger示例2 - 创建触发器的表达式,每5分钟触发一次,分钟后10秒(即上午10时10分,上午10::10等)。
“ / * * *?” CronTrigger示例3 - 在每个星期三和星期五的10:,:,:30和13:30创建触发器的表达式。
“ -?* WED,FRI“ CronTrigger示例4 - 创建触发器的表达式,每个月5日和20日上午8点至10点之间每半小时触发一次。请注意,触发器将不会在上午10点开始,仅在8:,:,:00和9:
“ / - , *?” 请注意,一些调度要求太复杂,无法用单一触发表示 - 例如“每上午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 创作不易,任何人或团体、机构全部转载或者部分转载、摘录,请在文章明显位置注明作者和原文链接。 |
震惊!Windows Service服务和定时任务框架quartz之间原来是这种关系……的更多相关文章
- quartz.net结合Topshelf实现windows service服务托管的作业调度框架
topshelf可以很简单方便的实现windows service服务,详见我的一篇博客的介绍 http://www.cnblogs.com/xiaopotian/articles/5428361.h ...
- C# Windows Service服务的创建和调试
前言 关于Windows服务创建和调试的文章在网络上的很多文章里面都有,直接拿过来贴在这里也不过仅仅是个记录,不会让人加深印象.所以本着能够更深刻了解服务项目的创建和调试过程及方法的目的,有了这篇记录 ...
- Windows Service 服务搭配FluentScheduler实现定时任务调度
Windows Service 服务 创建Windows Service 项目 创建一个Windows Service项目,并将项目名称改为 TaskWindowService 在解决方案资源管理器内 ...
- .Net Windows Service(服务) 调试安装及System.Timers.Timer 使用
Windows Service(服务) 是运行在后台的进程 1.VS建立 Windows 服务(.NET Framework) 2.添加Timer 双击Service1.cs可以拖控件(System ...
- C#制作Windows service服务系列二:演示一个定期执行的windows服务及调试(windows service)
系列一: 制作一个可安装.可启动.可停止.可卸载的Windows service(downmoon原创) 系列二:演示一个定期执行的windows服务及调试(windows service)(down ...
- [开发笔记]-Windows Service服务相关注意事项
注意一:报错:“本地计算机上的 *** 服务启动后停止.某些服务在未由其他服务或程序使用时将自动停止.” 该问题主要的原因是 Service服务程序中有错误. 遇到这个问题时,无论是重新安装服务,还是 ...
- war包部署在tomcat下,使用windows service服务方式启动tomcat服务器,在包含调用dll的模块,报dll找不到问题的解决办法
问题描述: 开发了一个需要调用dll的java web程序,在idea开发环境下运行调试没问题,可以正常运行,在tomcat/bin下,运行批处理startup.bat,启动tomcat服务器,也可以 ...
- 开源定时任务框架Quartz(一)
简介 Quartz是OpenSymphony开源组织的一个开源项目,定时任务框架,纯Java语言实现,最新版本为2.3.0. 设计模式 Quartz中使用的设计模式如下: 1.Builder模式 2. ...
- [开发笔记]-控制Windows Service服务运行
用代码实现动态控制Service服务运行状态. 效果图: 代码: #region 启动服务 /// <summary> /// 启动服务 /// </summary> /// ...
随机推荐
- FOC 算法基础之欧拉公式
文章目录 欧拉公式 几何意义 复数平面 动态过程 加法 FOC电压矢量的推导 总结 参考 FOC中电压矢量合成的推导,对于欧拉公式的几何意义做了一个全面的回顾. 欧拉公式 欧拉是一个天才,欧拉公式甚至 ...
- Wpf 正常关闭程序 Gc 来不及回收?
最近在使用Udp开发客户端,发现很长时间GC都无法回收UdpClient,所以我决定强制标记Gc,非常奇怪的是,毫无作用,在Windows任务管理器中,也看不见程序的身影.简单来说,gc是系统为你独立 ...
- JS的数据类型判断函数、数组对象结构处理、日期转换函数,浏览器类型判断函数合集
工具地址:https://github.com/BothEyes1993/bes-jstools bes-jstools 100多个基础常用JS函数和各种数据转换处理集合大全,此工具包是在 outil ...
- 使用jquery实现的自适应导航
话不多说,直接晒代码 <div class="headering"> <div class="header-top"> <div ...
- java实例化对象的几种方式
这里一共整理了四种方法 ①用类的new ()创建 ②用clone方法创建, 需要类实现 Cloneable 接口 ③通过反射机制创建 ④将一个对象实例化后,进行序列化,再反序列化 下面是实现代码 1) ...
- C#如何给WinForm的button等控件添加快捷键
网上有三种方法来设置快捷键,经本人验证后得出最优方法 Alt+*(按钮快捷键) 在大家给button.label.menuStrip等控件设置Text属性时在后边加&键 名就可以了,比如b ...
- 深入Spring之IOC之加载BeanDefinition
本文主要分析 spring 中 BeanDefinition 的加载,对于其解析我们在后面的文章中专门分析. BeanDefinition 是属于 Spring Bean 模块的,它是对 spring ...
- ql的python学习之路-day9
前言:本节主要学习装饰器 一.装饰器 定义:本质上是个函数,用来装饰其他函数:(就是为其他函数添加附加功能) 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 以上两点可以总 ...
- Kubernetes fabric8 JavaAPI
Kubernetes fabric8 JavaAPI 一.依赖准备 <dependency> <groupId>io.fabric8</groupId> <a ...
- poj3648 2-SAT进阶 记录点拓扑
Wedding Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 10556 Accepted: 3220 Specia ...