Topshelf实现对于控制台程序安装windows服务
1. 安装TopShelf程序包
在Program中写入:
var host = HostFactory.New(x => { x.Service<QuartzServiceRunner>(s => { s.ConstructUsing(name => new ServiceTask()); s.WhenStarted(p => p.Start()); s.WhenStopped(p => p.Stop()); }); x.RunAsLocalSystem(); x.SetDescription("Bley_QuartzTopShelf_Service"); x.SetDisplayName("QuartzTopShelfDemo服务"); x.SetServiceName("QuartzTopShelfDemoService"); }); host.Run();
QuartzServiceRunner 是自定义类
编译之后在bin下面的exe 文件 利用 cmd 去安装 ***.exe install 卸载 ***.exe uninstall (安装卸载比较方便)
2. Quartz 的使用:
利用nuget对Quartz包进行安装
Quartz Corn的表达式用法:
由7段构成:秒 分 时 日 月 星期 年(可选) 七段之间用空格隔开
"-" :表示范围 MON-WED表示星期一到星期三
"," :表示列举 MON,WEB表示星期一和星期三
"*" :表是“每”,每月,每天,每周,每年等
"/" :表示增量:0/15(处于分钟段里面) 每15分钟,在0分以后开始,3/20 每20分钟,从3分钟以后开始
"?" :只能出现在日,星期段里面,表示不指定具体的值
"L" :只能出现在日,星期段里面,是Last的缩写,一个月的最后一天,一个星期的最后一天(星期六)
"W" :表示工作日,距离给定值最近的工作日
"#" :表示一个月的第几个星期几,例如:"6#3"表示每个月的第三个星期五(1=SUN...6=FRI,7=SAT)
Quartz的几个概念:
IScheduler 任务调度器
IJobDetail Ijob 任务
ITrigger Trigger 触发器
单个Job的执行:
<appSettings> <add key="cronExpr" value="0/5 * * * * ?"/> </appSettings>
public class QuartzServiceRunner { private readonly IScheduler scheduler; public QuartzServiceRunner() { scheduler = StdSchedulerFactory.GetDefaultScheduler(); } public void Start() { //从配置文件中读取任务启动时间 string cronExpr = ConfigurationManager.AppSettings["cronExpr"]; IJobDetail job = JobBuilder.Create<DeleteDomainJob>().WithIdentity("job1", "group1").Build(); //创建任务运行的触发器 ITrigger trigger = TriggerBuilder.Create() .WithIdentity("triggger1", "group1") .WithSchedule(CronScheduleBuilder.CronSchedule(new CronExpression(cronExpr))) .Build(); //启动任务 scheduler.ScheduleJob(job, trigger); scheduler.Start(); } public void Stop() { scheduler.Clear(); } public bool Continue(HostControl hostControl) { scheduler.ResumeAll(); return true; } public bool Pause(HostControl hostControl) { scheduler.PauseAll(); return true; } }
public class DeleteDomainJob : IJob { //readonly ILog _log = LogManager.GetLogger(typeof(DeleteDomainJob)); public void Execute(IJobExecutionContext context) { NlogHelper.LogInfo("start job -----------------------"); System.Threading.Thread.Sleep(1000 * 3); NlogHelper.LogInfo("end job -------------------------"); } }
public class DeleteDomainJob : IJob { //readonly ILog _log = LogManager.GetLogger(typeof(DeleteDomainJob)); public void Execute(IJobExecutionContext context) { NlogHelper.LogInfo("start job -----------------------"); System.Threading.Thread.Sleep(1000 * 3); NlogHelper.LogInfo("end job -------------------------"); } }
对于Job 我们一般肯定是希望可以同时在这个项目中配置出多个job
可以考虑做一个JobCollection类来封装配置文件
配置文件如下:
<configSections> <section name="JobSettings" type="ServiceJobHosts.JobSettings,ServiceJobHosts"/> </configSections> <JobSettings> <add type="ServicesJobs.GuessSendPrizeJob, ServicesJobs" cron="0/5 * * * * ?" triggerInstantly="true"/> <add type="ServicesJobs.LottoSendPrizeJob, ServicesJobs" cron="0/5 * * * * ?" triggerInstantly="true"/> </JobSettings>
public class GuessSendPrizeJob : IJob { public void Execute(IJobExecutionContext context) { NlogHelper.LogInfo("Start Guess Send Prize Job--------------"); System.Threading.Thread.Sleep(10000); NlogHelper.LogInfo("End Guess Send Prize Job --------------"); } }
public class LottoSendPrizeJob : IJob { public void Execute(IJobExecutionContext context) { //log start NlogHelper.LogError("Start Lotto Send Prize Job--------------"); System.Threading.Thread.Sleep(10000); NlogHelper.LogError("End Lotto Send Prize Job --------------"); } }
public class JobWrapper { /// <summary> /// 任务详情 /// </summary> public IJobDetail JobDetail { get; set; } /// <summary> /// 任务触发器 /// </summary> public ITrigger Trigger { get; set; } /// <summary> /// 是否立即触发 /// </summary> public bool TriggerInstantly { get; set; } }
/// <summary> /// 定时任务配置类 /// </summary> public class JobSettings : ConfigurationSection { [ConfigurationProperty("", IsDefaultCollection = true)] public JobTypeElementCollection JobTypes { get { return (JobTypeElementCollection)this[""]; } } public static JobSettings GetSection() { return ConfigurationManager.GetSection("JobSettings") as JobSettings; } } /// <summary> /// 定时任务配置集合 /// </summary> public class JobTypeElementCollection : ConfigurationElementCollection { protected override ConfigurationElement CreateNewElement() { return new JobTypeElement(); } protected override object GetElementKey(ConfigurationElement element) { JobTypeElement serviceTypeElement = (JobTypeElement)element; return serviceTypeElement.JobType.MetadataToken; } } /// <summary> /// 单个定时任务配置类 /// </summary> public class JobTypeElement : ConfigurationElement { /// <summary> /// cron表达式 /// </summary> [ConfigurationProperty("cron", IsRequired = true)] public string CronExpression { get { return this["cron"] as string; } set { this["cron"] = value; } } /// <summary> /// 是否立即触发 /// </summary> [ConfigurationProperty("triggerInstantly", IsRequired = true)] public bool TriggerInstantly { get { return (bool)this["triggerInstantly"]; } set { this["triggerInstantly"] = value; } } /// <summary> /// 定时job的类型 /// </summary> [ConfigurationProperty("type", IsRequired = true)] [TypeConverter(typeof(AssemblyQualifiedTypeNameConverter))] public Type JobType { get { return (Type)this["type"]; } set { this["type"] = value; } } }
public class AssemblyQualifiedTypeNameConverter : ConfigurationConverterBase { public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { Type result = null; string typeName = value as string; if (!string.IsNullOrWhiteSpace(typeName)) { result = Type.GetType(typeName, false); if (result == null) { throw new ArgumentException(string.Format("不能加载类型\"{0}\"", typeName)); } } return result; } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { Type type = value as Type; if (type == null) { throw new ArgumentNullException("value"); } return type.AssemblyQualifiedName; } }
public class JobCollection : Collection<JobWrapper> { #region Fields & Properties /// <summary> /// 作业调度器工厂 /// </summary> private static ISchedulerFactory schedulerFactory; /// <summary> /// 作业调度器 /// </summary> private static IScheduler scheduler; #endregion private class Nested { static Nested() { } internal static readonly JobCollection Instance = new JobCollection(); } public static JobCollection Instance { get { return Nested.Instance; } } /// <summary> /// 静态构造器 /// </summary> static JobCollection() { try { schedulerFactory = new StdSchedulerFactory(); scheduler = schedulerFactory.GetScheduler(); } catch(Exception ex) { int m = 2; } } /// <summary> /// 私有构造器 /// </summary> /// <param name="serviceTypes"></param> private JobCollection() { JobSettings settings = JobSettings.GetSection(); if (settings == null) { //Log.WriteLog("未能获取到定时任务配置信息!"); return; } foreach (JobTypeElement element in settings.JobTypes) { if (string.IsNullOrWhiteSpace(element.CronExpression)) { continue; } try { string jobName = element.JobType.Name; JobKey jobKey = new JobKey("job_" + jobName); IJobDetail jobDetail = JobBuilder.Create(element.JobType) .WithIdentity(jobKey) .Build(); //触发器 /* WithMisfireHandlingInstructionDoNothing()方法与DisallowConcurrentExecution特性配合使用, * 可以起到如下作用:同一Job如果上一轮执行还未完成,则本次不触发。 */ ITrigger trigger = TriggerBuilder.Create() .WithIdentity(string.Format("trigger_{0}", jobName)) .WithSchedule(CronScheduleBuilder.CronSchedule(element.CronExpression).WithMisfireHandlingInstructionDoNothing()) .Build(); JobWrapper job = new JobWrapper { JobDetail = jobDetail, Trigger = trigger, TriggerInstantly = element.TriggerInstantly }; this.Add(job); } catch (Exception ex) { string msg = string.Format("加载定时任务{0}时发生异常:{1}", element.JobType.Name, ex.Message); //Log.WriteLog(msg); } } } /// <summary> /// 启动集合中所有定时任务 /// </summary> public void Start() { scheduler.Start(); foreach (JobWrapper job in this) { try { scheduler.ScheduleJob(job.JobDetail, job.Trigger); if (job.TriggerInstantly) { scheduler.TriggerJob(job.JobDetail.Key); } //File.AppendAllText(sysLogPath, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "服务" + job.JobDetail.Key.Name + "顺利启动 \r\n"); } catch (Exception ex) { //File.AppendAllText(sysLogPath, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + ex.Message + "\r\n" + ex.StackTrace + "\r\n"); } } } /// <summary> /// 关闭集合中所有定时任务 /// </summary> public void Stop() { foreach (JobWrapper job in this) { try { JobKey jobKey = job.JobDetail.Key; string treggerName = "trigger_" + jobKey.Name.Substring(jobKey.Name.IndexOf("_") + 1); TriggerKey triggerKey = new TriggerKey(treggerName); scheduler.PauseTrigger(triggerKey); scheduler.UnscheduleJob(triggerKey); scheduler.DeleteJob(jobKey); //File.AppendAllText(sysLogPath, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "服务" + job.JobDetail.Key.Name + "已经停止 \r\n"); } catch (Exception ex) { //File.AppendAllText(sysLogPath, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + ex.Message + "\r\n" + ex.StackTrace + "\r\n"); } } scheduler.Shutdown(false); } }
public class ServiceTask { public void Start() { JobCollection.Instance.Start(); } public void Stop() { JobCollection.Instance.Stop(); } }
在Program.cs中加入如下代码:
var host = HostFactory.New(x => { x.Service<ServiceTask>(s => { s.ConstructUsing(name => new ServiceTask()); s.WhenStarted(p => p.Start()); s.WhenStopped(p => p.Stop()); }); x.RunAsLocalSystem(); x.SetDescription("Bley_QuartzTopShelf_Service"); x.SetDisplayName("QuartzTopShelfDemo服务"); x.SetServiceName("QuartzTopShelfDemoService"); }); host.Run();