github地址:https://github.com/fluentscheduler/FluentSchedulergit
FluentScheduler是一个简单的任务调度框架,使用起来很是方便,这个框架也是我在搜索iis预加载的时候偶然间发现的,立马拿来试用一下,感受爽呆了,固然还有Quarz.Net之类的其余任务管理框架,不过看配置彷佛有点麻烦,反正除了timer我啥也没用过...github
以前还花费了很长一段时间本身写了一套定时任务的框架,现在看到FluentScheduler我已经决定将以前的废弃了...web
好吧,废话很少说,框架调用很是简单,因此直接上代码了,其实我作的只不过是把英文翻译一下服务器
.net 框架:.net framework 4.5架构
项目:.net mvc5mvc
若是要在winform,wpf之类的项目中使用是彻底没有问题的,由于本文最终的目标是实现将该web项目做为一个定时任务的服务,因此选择了以上的架构框架
1.引用nuget包:FluentSchedulerasp.net
2.Application_Start函数加上:dom
//初始化任务管理器 JobManager.Initialize(new MyRegistry());
3.MyRegistry.cs编辑器
public class MyRegistry : Registry { public MyRegistry() { // Schedule an IJob to run at an interval // 当即执行每10秒一次的计划任务。(指定一个时间间隔运行,根据本身需求,能够是秒、分、时、天、月、年等。) Schedule<MyJob>().ToRunNow().AndEvery(10).Seconds(); // 当即执行每10秒一次的计划任务。若是本次任务没有结束,下一次的任务则不会开始,禁止并行运行 Schedule<MyJob>().NonReentrant().ToRunNow().AndEvery(10).Seconds(); //在天天的21:15执行计划任务 Schedule(() => Console.WriteLine("It's 9:15 PM now.")).ToRunEvery(1).Days().At(21, 15); // 当即执行一个在每个月的第一个星期一 3:00 的计划任务 Schedule(() => Console.WriteLine("It's 3:00 AM now.")).ToRunNow().AndEvery(1).Months().OnTheFirst(DayOfWeek.Monday).At(3, 0); //在每周一的21:15执行计划任务 Schedule(() => Console.WriteLine("It's 9:15 PM now.")).ToRunEvery(1).Weeks().On(DayOfWeek.Monday).At(21, 15); } }
上面须要注意的是NonReentrant
函数的使用,在某些特殊的业务里可能任务执行的时间比定时循环的间隔时间要长,这时候你就要考虑是否容许并行运行两个一样的任务,NonReentrant就是用来解决这个问题的
4.MyJob.cs
public class MyJob : IJob, IRegisteredObject { private readonly object _lock = new object(); private bool _shuttingDown; private static Logger logger = LogManager.GetCurrentClassLogger(); //初始化日志类 public MyJob() { HostingEnvironment.RegisterObject(this); } public void Execute() { try { lock (_lock) { if (_shuttingDown) return; logger.Info("开始工做:" + DateTime.Now); Thread.Sleep(60*1000); logger.Info("工做结束:" + DateTime.Now); } } finally { HostingEnvironment.UnregisterObject(this); } } public void Stop(bool immediate) { logger.Info("调用stop:" + DateTime.Now); lock (_lock) { logger.Info("lock结束:" + DateTime.Now); _shuttingDown = true; } HostingEnvironment.UnregisterObject(this); } }
上面是一个简单的示例,全部的业务逻辑都在Execute函数中执行,若是不在web项目中运行,则不须要实现IRegisteredObject接口以及stop函数,全部的业务代码均在Execute
函数中执行
在以前咱们也有部分项目用widowsservice来作定时任务,可是弊端很明显,调试太麻烦,发布也麻烦,自动发布更难实现
相比之下web服务器就容易管理的多了
实际上在asp.net 中的定时任务和FluentScheduler框架并无什么必然的联系,你也能够用timer或其余的任何方式来实现,可是全部的这些实现方式都避免不了面对一个问题:IIS的回收机制
由于有了回收机制的存在,因此在asp.net中作定时任务就会面临两个问题:
1.任务没有执行完成线程就被回收了
2.线程回收以后,只有在下一次访问网站的时候任务才会再次启动
首先咱们来解决第一个问题:
对于iis的回收,咱们须要作的其实并非阻止它的回收,实际上我试过各类方式都没法彻底阻挡iis的回收,不知道是不是方法没有用对。
可是咱们能够保证当前的任务执行完毕再进行回收
方式就是实现IRegisteredObject
接口,以上面的MyJob类为例,咱们经过调用HostingEnvironment.RegisterObject方法在ASP.NET中注册它
经过调用HostingEnvironment.UnregisterObject方法释放服务
当Appdomain要被回收的时候,会调用已注册对象IRegisteredObject中的Stop方法。
// // 摘要: // Requests a registered object to unregister. // // 参数: // immediate: // true to indicate the registered object should unregister from the hosting environment // before returning; otherwise, false. void Stop(bool immediate);
在第一次调用stop方法时,参数为false,执行完毕后,若是没有调用HostingEnvironment.UnregisterObject
函数,隔30秒stop方法会再次被调用,参数为true,若是仍然没有调用HostingEnvironment.UnregisterObject
函数,该服务就会被移除
不过咱们使用的过程当中并不会考虑第二次的调用,由于在第一次stop函数被调用的时候咱们就会lock住正在执行的任务,而且一直到任务执行完成再释放lock,最后调用HostingEnvironment.UnregisterObject保证任务正常退出
对于这个流程上面的Myjob就是FluentScheduler提供的一个示例
IIS预加载
应用程序池回收以后,若是没有人访问网站,w3wp是不会启动的,那也就表明着咱们的定时任务就不会启动了,因此咱们须要在程序池被回收以后模拟访问一下该网站,咱们能够经过写一个定时的程序每隔一秒钟访问一遍该网站来解决这个问题,可是为了解决这个问题多写一个程序并无必要,由于微软已经提供了一个网站预加载的功能,每当应用程序池被回收,系统就会启动一个进程模拟访问一遍网站。这个功能彷佛是iis7以后就有了,我下面演示的iis10的界面,其余版本的界面可能会稍微有所不一样
1.修改应用程序池启动模式
2.开启对应网站预加载
3.增长配置编辑器,编写默认预加载的请求页面
至此,咱们的服务就能够正常的运行啦