1. 程式人生 > >使用ASP.NET實現Windows Service定時執行任務

使用ASP.NET實現Windows Service定時執行任務

我們怎樣才能在伺服器上使用asp.NET定時執行任務而不需要安裝windows service?我們經常需要執行一些維護性的任務或者像傳送提醒郵件給使用者這樣的定時任務。這些僅僅通過使用Windows Service就可以完成。Asp.Net通常是一個無狀態的提供程式,不支援持續執行程式碼或者定時執行某段程式碼。所以,我們不得不構建自己的windows service來執行那些定時任務。但是在一個共享的託管環境下,我們並不總是有機會部署我們自己的windwos service到我們託管服務提供商的web伺服器上。我們要麼買一個專用的伺服器,當然這是非常昂貴的,要麼就犧牲我們網站的一些功能。然而,執行一個定期執行的任務是一個非常有用的功能,特別是對那些需要傳送提醒郵件的使用者、需要維護報表以及執行清理操作的的管理員而言。我將給你展示一種無須使用任何windows service,僅僅採用asp.net來執行定期任務的方式。

它怎樣工作

首先,我們需要asp.net中的某些“場景”,能夠持續不斷地執行並且給我們一個回撥。而IIS上的web伺服器就是一個很不錯的選擇。所以,我們需要從它那裡很“頻繁”地獲得回撥,這樣我們可以檢視一個任務佇列,並且能夠看到是否有任務需要執行。現在,這裡有一些方式可以為我們獲得對web伺服器的“操作權”:

(1)     當一個頁面被請求

(2)     當一個應用程式被啟動

(3)     當一個應用程式被停止

(4)     當一個會話開啟、結束或者超時

(5)     當一個快取項失效

一個頁面被請求是隨機的。如果幾個小時內沒有人訪問你的站點,那麼幾個小時內你都無法完成任何“任務”。另外,一個請求的執行時間是非常短的,並且它本身也需要越快越好。如果你計劃在頁面請求的時候執行“計劃任務”,這樣頁面將會被迫執行很長時間,這將導致一個很糟糕的使用者體驗。所以,選擇在頁面請求的時機做這樣的操作不是一個好的選擇。

一個頁面被請求是隨機的。如果幾個小時內沒有人訪問你的站點,那麼幾個小時內你都無法完成任何“任務”。另外,一個請求的執行時間是非常短的,並且它本身也需要越快越好。如果你計劃在頁面請求的時候執行“計劃任務”,這樣頁面將會被迫執行很長時間,這將導致一個很糟糕的使用者體驗。所以,選擇在頁面請求的時機做這樣的操作不是一個好的選擇。

當一個應該程式啟動時,Global.asax內的Application_Start方法給我們提供了一個回撥。所以這是一個開啟後臺執行緒的好地方,後臺執行緒可以永久執行以執行“計劃任務”。然而,當該執行緒在web伺服器由於零負載而“休息”一會兒的時候,卻可能被隨時“殺死”。

當一個應用程式停止的時候,我們同樣可以從Application_End方法獲得一個回撥。但是我們在這裡卻不能做任何事情,因為整個應該程式都已經快要結束運行了。Global.asax裡的Session_Start會在當一個使用者訪問一個需要被例項化為新會話的頁面時被觸發。所以這也是一個隨機事件。而我們需要一個能持久且定期執行的“場景”。

一個快取項的失效可以提供一個時間點或持續時間。在asp.net中你可以在Cache物件中增加一個實體,並且可以設定一個絕對失效時間,或者設定當其被從快取中移除後失效。你可以利用下面的Cache類中的方法來做這些:
  1. publicvoid Insert ( System.String key , System.Object value ,   
  2.                      System.Web.Caching.CacheDependency dependencies ,   
  3.                      System.DateTime absoluteExpiration ,   
  4.                      System.TimeSpan slidingExpiration ,   
  5.                      System.Web.Caching.CacheItemPriority priority ,   
  6.                      System.Web.Caching.CacheItemRemovedCallback onRemoveCallback )  

onRemoveCallback是一個方法的委託,該方法在一個快取項失效時被呼叫。在該方法中,我們可以做任何我們想做的事情。所以,這是一個定期、持續執行程式碼而不需要任何頁面請求的很好的候選。

這意味著,我們可以在一個快取項失效時模擬一個簡單的windows service。

建立快取項的回撥

首先,在Application_Start中,我們需要註冊一個快取項,並讓它在兩分鐘後失效。請注意,你設定回撥的失效時間的最小值是兩分鐘。儘管你可以設定一個更小的值,但它似乎不會工作。出現該問題最大的可能是,asp.net工作程序每兩分鐘才檢視一次快取項。

  1. privateconststring DummyCacheItemKey = "GagaGuguGigi";  
  2. protectedvoid Application_Start(Object sender, EventArgs e)  
  3. {  
  4.     RegisterCacheEntry();  
  5. }  
  6. privatebool RegisterCacheEntry()  
  7. {   
  8.     ifnull != HttpContext.Current.Cache[ DummyCacheItemKey ] ) returnfalse;  
  9.     HttpContext.Current.Cache.Add( DummyCacheItemKey, "Test"null,   
  10.         DateTime.MaxValue, TimeSpan.FromMinutes(1),   
  11.         CacheItemPriority.Normal,  
  12.         new CacheItemRemovedCallback( CacheItemRemovedCallback ) );  
  13.     returntrue;  
  14. }  

該快取實體是一個虛設的實體。我們不需要在這裡儲存任何有價值的資訊,因為無論我們在這裡儲存什麼,他們都有可能在應用程式重啟時丟失。另外,我們所需要的只是使該項的頻繁回撥。

在回撥的內部,我們就可以完成“計劃任務”:

  1. publicvoid CacheItemRemovedCallback( string key,   
  2.             object value, CacheItemRemovedReason reason)  
  3. {  
  4.     Debug.WriteLine("Cache item callback: " + DateTime.Now.ToString() );  
  5.     DoWork();  
  6. }  

在快取項失效時再次儲存快取項

無論何時快取項失效,我們都能夠獲得一個回撥同時該項將永久地從快取中消失。所以,我們將不能再次獲得回調了。為了能提供一個持續的回撥,我們需要在下次失效之前重新儲存一個快取項。這看起來似乎相當容易:我們可以在回撥函式中呼叫我們上面展示的RegisterCacheEntry方法,可以這麼做嗎?它不會工作!當回調發生,HttpContext已經無法訪問。HttpContext僅僅在一個請求正在被處理的時候才可以被訪問。因為回調發生在web伺服器的幕後,所以這裡沒有請求需要被處理,因而HttpContext物件無法獲得。因此,你也無法從回撥中訪問Cache物件。

方案是,我們需要一個簡單的請求。我們可以利用.netFramework中的WebClient類來實現一個對虛擬頁面的“虛擬”訪問。當虛擬頁面被執行,我們可以Hold住HttpContext物件,然後再次註冊一個快取項的回撥。

所以,回撥方法作一點修改來發出一個虛擬呼叫。

  1. publicvoid CacheItemRemovedCallback( string key,   
  2.             object value, CacheItemRemovedReason reason)  
  3. {  
  4.     Debug.WriteLine("Cache item callback: " + DateTime.Now.ToString() );  
  5.     HitPage();  
  6.     // Do the service works
  7.     DoWork();  
  8. }  

HitPage方法對一個虛擬頁面發出呼叫:

  1. privateconststring DummyPageUrl =   
  2.     "http://localhost/TestCacheTimeout/WebForm1.aspx";  
  3. privatevoid HitPage()  
  4. {  
  5.     WebClient client = new WebClient();  
  6.     client.DownloadData(DummyPageUrl);  
  7. }  
無論虛擬頁面在什麼時候被呼叫,Application_BeginRequest方法都將被呼叫。在那裡,我們可以核查是否它是一個“虛擬”頁面。
  1. protectedvoid Application_BeginRequest(Object sender, EventArgs e)  
  2. {  
  3.     // If the dummy page is hit, then it means we want to add another item
  4.     // in cache
  5.     if( HttpContext.Current.Request.Url.ToString() == DummyPageUrl )  
  6.     {  
  7.        // Add the item in cache and when succesful, do the work.
  8.        RegisterCacheEntry();  
  9.     }  
  10. }  

我們僅僅截獲虛擬頁面的請求,並且讓其他的頁面以他們原來的方式繼續執行。

Web程序重啟時重啟快取項回撥

這裡有很多情況,可能導致web伺服器重啟。例如,如果系統管理員重啟IIS,或者重啟電腦,或者web程序陷入死迴圈(在windows 2003下)。在這樣的情況下,服務將停止執行,直到一個頁面被請求和Application_Start被呼叫。Application_Start僅僅在當一個頁面第一次被訪問時才會被呼叫。所以,當web程序被重啟時為了讓“服務”執行起來,我們只能手動呼叫“虛擬”頁面,或者某人需要訪問你站點的主頁。

一個“滑頭”的方案是:可以把搜尋引擎加入你的站點中。搜尋引擎時常會爬行頁面。因此,它們將訪問你站點的一個網頁,這就可以觸發Application_Start的執行,因此服務將被再次啟動執行。

另一個方案是向某些通訊或可用性監控服務註冊你的站點。有許多關注你站點以及可以檢查你的站點是否正常並且效能是否良好的Web 服務。所有這些服務都需要訪問你站點的頁面然後收集統計資訊。所以,通過註冊這樣的服務,你可以保證你的站點一直“存活”著。

測試可執行任務的型別

讓我們來測試一下,是否我們能夠做一個windowsservice能夠做的一切任務。首先,第一個問題是,我們不能做一個windows service能夠做的所有事情,因為windowsservice執行在一個本地系統賬戶的許可權下。這是一個具有非常高許可權的賬戶,使用這個賬戶你可以在你的系統中做任何事情。然而,asp.net web執行緒執行在ASPNET賬戶下(windows xp)或者NETWORKSERVICE賬戶下(windows 2003)。這是一個低許可權的賬戶,並且沒有許可權訪問硬碟。為了允許服務向硬碟寫東西,web程序需要被授予對資料夾的寫許可權。我們都知道關於此的安全問題,所以我將不再詳述細節。

現在,我們將開始測試我們通常利用windowsservice完成的事情:

(1)    向檔案寫東西

(2)    資料庫操作

(3)    Web Service呼叫

(4)    MSMQ 操作

(5)    Email 傳送

讓我們來寫一些測試程式碼:

  1. privatevoid DoWork()  
  2. {  
  3.     Debug.WriteLine("Begin DoWork...");  
  4.     Debug.WriteLine("Running as: " +   
  5.           WindowsIdentity.GetCurrent().Name );  
  6.     DoSomeFileWritingStuff();  
  7.     DoSomeDatabaseOperation();  
  8.     DoSomeWebserviceCall();  
  9.     DoSomeMSMQStuff();  
  10.     DoSomeEmailSendStuff();  
  11.     Debug.WriteLine("End DoWork...");  
  12. }  

測試檔案“寫”操作

讓我們來測試一下是否我們真的能夠向檔案內寫東西。在C盤建立一個資料夾,將其命名為“temp”(如果磁碟的格式是NTFS,允許ASPNET/NETWORKSERVICE賬戶向該資料夾的寫許可權)。
  1. privatevoid DoSomeFileWritingStuff()  
  2. {  
  3.     Debug.WriteLine("Writing to file...");  
  4.     try
  5.     {  
  6.        using( StreamWriter writer =   
  7.         new StreamWriter(@"c:\temp\Cachecallback.txt"true) )  
  8.        {  
  9.         writer.WriteLine("Cache Callback: {0}", DateTime.Now);  
  10.         writer.Close();  
  11.        }  
  12. 相關推薦

    使用ASP.NET實現Windows Service定時執行任務

    我們怎樣才能在伺服器上使用asp.NET定時執行任務而不需要安裝windows service?我們經常需要執行一些維護性的任務或者像傳送提醒郵件給使用者這樣的定時任務。這些僅僅通過使用Windows Service就可以完成。Asp.Net通常是一個無狀態的提供

    android使用AlarmManager實現應用每天定時執行任務

    簡介: 關於service 大家應都知道是Android 四大元件之一,用來執行後臺任務的。 Android 中的定時任務一般有兩種實現方式,一種是使用Java API 裡提供的Timer 類,一種是使用Android 的Alarm 機制。 那麼首先我們來

    ASP.NET MVC計劃任務實現方法(定時執行某個功能)

    AR tar 註意 eve 文件 ebr send npr 如何實現 系統中定時執行某個任務是比較常用的功能,如一個部門定期向上級部門上報數據是一個典型的例子,下面就簡單說說在.net mvc中如何實現定時執行某個功能的方法。 1、首先修改Glocal.asax文件,在A

    基於MVC 的Quartz.Net組件實現定時執行任務調度

    創建 .get 關系 成了 star +++ rep 清除 pub 新建mvc項目之後,首先引用Quartz組件。工具-->NuGet包管理器-->管理解決方案的 NuGet包管理器 組件安裝完成。 Quartz.Net一個最簡單任務至少包括三部分實現:

    Java定時任務:利用java Timer類實現定時執行任務的功能

    lpad 虛擬 觀察 exce 就是 set ring 構造 trac 一、概述 在java中實現定時執行任務的功能,主要用到兩個類,Timer和TimerTask類。其中Timer是用來在一個後臺線程按指定的計劃來執行指定的任務。 TimerTask一個抽象類,它的子類代

    schtasks 定時執行任務 windows 平臺

    schtasks 定時執行任務 windows 平臺 遇到一個在windows 平臺執行定時指令碼任務的問題,開始的時候用 at ,但是一直執行不成功。 偶然在網上搜索到 schtasks 命令,下面是參考文件地址: schtask 執行定時任務 下面就來看一下怎麼使用 sc

    asp.net core 2.0 定時任務

    介紹TimeJob 首先專案新增引用   然後註冊服務並使用: services.AddTimedJob();//註冊TimeJob服務 app.UseTimedJob();//使用timeJob 編寫類,繼承自Pomelo.AspNetCore.TimedJob.Job

    使用AlarmManager實現Android應用每天定時執行任務

    介紹 android官方文件:AlarmManager 在Android平臺,除了使用AlarmManger外,還可以使用Timer或者Handler來實現定時任務,但這兩種方式定時並不會太準確;因此如果我們需要實現精準定時任務,使用AlarmManger更

    javaWeb監聽器結合定時實現定時執行任務

    java監聽器 通過監聽器可以自動的激發一些操作,比如監聽線上人數; 監聽器介面有: ServletContextListener——監聽ServletContext物件; HttpSessionL

    ThinkPHP實現定時執行任務的兩種方法

        $this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微軟雅黑"; color: #

    Java—實現每天定時執行任務

    1、定義TimerManager類 import java.util.Calendar; import java.util.Date; import java.util.Timer; /** * java定時任務,每天定時執行任務 * @

    Mysql定時執行任務實現方法

    在開發過程中經常會遇到這樣一個問題: 每天必須定時去執行一條sql語句 或更新或刪除或執行特定的sql語句。 mysql的客戶端工具Navicat for MySQL提供了計劃任務的形式,可以很好地實現該功能,而且也確實可行,比如像定時備份就做的很好。 然而當在執行

    寫一個Windows Service定時任務

    本次將做一個Windows Serice定時任務,具體步驟,以下將一一講解 1.開啟VS2013,選擇檔案->新建->專案。 2.在專案中找到【windows服務】專案,重新命名後點擊確

    使用tornado實現定時執行任務

    使用tornado實現輪詢:from tornado import web, ioloop import datetime class MainHandler(web.RequestHandler): def get(self): self.wri

    Java實現每天定時執行任務

    init 開始 java實現 int 周期 cal ner 你是 nth 1、TimerManager類 package com.lyz.util; import java.util.Calendar; import java.util.Date; import jav

    ASP.NET實現支付寶接口功能

    接口 系統 func error 程序 dex ges img user 最近做系統,需要實現在線支付功能,毫不猶豫,選擇的是支付寶的接口支付功能。這裏我用的是即時到帳的接口,具體實現的步驟如下: 一、下載支付寶接口包 下載地址:

    C# asp.net實現文件上傳

    function asp.net visual 開發 null 前端代碼: 使用visual studio開發實現文件上傳 前端頁面代碼: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.as

    java中定時執行任務

    views sch start bsp tails pri ted java file 現在項目中用到需要定時去檢查文件是否更新的功能。timer正好用於此處。 用法很簡單,new一個timer,然後寫一個timertask的子類即可。 代碼如下: [java] vie

    Asp.net Core Windows部署

    安裝 應用 load core build 應用程序 post hosting .net 一、 IIS 部署模式 1. 安裝IIS服務 2. 下載安裝Core SDK https://www.microsoft.com/net/download/Wind

    crontab 定時執行任務

    usr pac 啊啊啊 oca CA 日期 and 基本 ron 今天腦殘了,公司有個任務要每小時執行一次,然後我就 /1 * commond結果被老大啪啪打臉了,emmmm前面的分鐘寫的星號,肯定是每分鐘啊啊啊啊啊啊啊啊啊啊,所以再來學習一遍這個命令。 基本