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.增長配置編輯器,編寫默認預加載的請求頁面
至此,咱們的服務就能夠正常的運行啦