使用ASP.NET實現(xiàn)Windows Service定時執(zhí)行任務(wù)
當前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
我們怎樣才能在服務(wù)器上使用asp.net定時執(zhí)行任務(wù)而不需要安裝windows service?我們經(jīng)常需要運行一些維護性的任務(wù)或者像發(fā)送提醒郵件給用戶這樣的定時任務(wù)。這些僅僅通過使用Windows Service就可以完成。Asp.net通常是一個無狀態(tài)的提供程序,不支持持續(xù)運行代碼或者定時執(zhí)行某段代碼。所以,我們不得不構(gòu)建自己的windows service來運行那些定時任務(wù)。但是在一個共享的托管環(huán)境下,我們并不總是有機會部署我們自己的windwos service到我們托管服務(wù)提供商的web服務(wù)器上。我們要么買一個專用的服務(wù)器,當然這是非常昂貴的,要么就犧牲我們網(wǎng)站的一些功能。然而,運行一個定期執(zhí)行的任務(wù)是一個非常有用的功能,特別是對那些需要發(fā)送提醒郵件的用戶、需要維護報表以及運行清理操作的的管理員而言。我將給你展示一種無須使用任何windows service,僅僅采用asp.net來運行定期任務(wù)的方式。
它怎樣工作 首先,我們需要asp.net中的某些“場景”,能夠持續(xù)不斷地運行并且給我們一個回調(diào)。而IIS上的web服務(wù)器就是一個很不錯的選擇。所以,我們需要從它那里很“頻繁”地獲得回調(diào),這樣我們可以查看一個任務(wù)隊列,并且能夠看到是否有任務(wù)需要執(zhí)行。現(xiàn)在,這里有一些方式可以為我們獲得對web服務(wù)器的“操作權(quán)”: (1) 當一個頁面被請求 (2) 當一個應(yīng)用程序被啟動 (3) 當一個應(yīng)用程序被停止 (4) 當一個會話開啟、結(jié)束或者超時 (5) 當一個緩存項失效 一個頁面被請求是隨機的。如果幾個小時內(nèi)沒有人訪問你的站點,那么幾個小時內(nèi)你都無法完成任何“任務(wù)”。另外,一個請求的執(zhí)行時間是非常短的,并且它本身也需要越快越好。如果你計劃在頁面請求的時候執(zhí)行“計劃任務(wù)”,這樣頁面將會被迫執(zhí)行很長時間,這將導(dǎo)致一個很糟糕的用戶體驗。所以,選擇在頁面請求的時機做這樣的操作不是一個好的選擇。 一個頁面被請求是隨機的。如果幾個小時內(nèi)沒有人訪問你的站點,那么幾個小時內(nèi)你都無法完成任何“任務(wù)”。另外,一個請求的執(zhí)行時間是非常短的,并且它本身也需要越快越好。如果你計劃在頁面請求的時候執(zhí)行“計劃任務(wù)”,這樣頁面將會被迫執(zhí)行很長時間,這將導(dǎo)致一個很糟糕的用戶體驗。所以,選擇在頁面請求的時機做這樣的操作不是一個好的選擇。 當一個應(yīng)該程序啟動時,Global.asax內(nèi)的 當一個應(yīng)用程序停止的時候,我們同樣可以從Application_End方法獲得一個回調(diào)。但是我們在這里卻不能做任何事情,因為整個應(yīng)該程序都已經(jīng)快要結(jié)束運行了。Global.asax里的Session_Start會在當一個用戶訪問一個需要被實例化為新會話的頁面時被觸發(fā)。所以這也是一個隨機事件。而我們需要一個能持久且定期運行的“場景”。 一個緩存項的失效可以提供一個時間點或持續(xù)時間。在 asp.net 中你可以在 Cache 對象中增加一個實體,并且可以設(shè)置一個絕對失效時間,或者設(shè)置當其被從緩存中移除后失效。你可以利用下面的 Cache 類中的方法來做這些:
這意味著,我們可以在一個緩存項失效時模擬一個簡單的windows service。 創(chuàng)建緩存項的回調(diào) 首先,在Application_Start中,我們需要注冊一個緩存項,并讓它在兩分鐘后失效。請注意,你設(shè)置回調(diào)的失效時間的最小值是兩分鐘。盡管你可以設(shè)置一個更小的值,但它似乎不會工作。出現(xiàn)該問題最大的可能是,asp.net工作進程每兩分鐘才查看一次緩存項。
該緩存實體是一個虛設(shè)的實體。我們不需要在這里存儲任何有價值的信息,因為無論我們在這里存儲什么,他們都有可能在應(yīng)用程序重啟時丟失。另外,我們所需要的只是使該項的頻繁回調(diào)。 在回調(diào)的內(nèi)部,我們就可以完成“計劃任務(wù)”:
在緩存項失效時再次存儲緩存項 無論何時緩存項失效,我們都能夠獲得一個回調(diào)同時該項將永久地從緩存中消失。所以,我們將不能再次獲得回調(diào)了。為了能提供一個持續(xù)的回調(diào),我們需要在下次失效之前重新存儲一個緩存項。這看起來似乎相當容易:我們可以在回調(diào)函數(shù)中調(diào)用我們上面展示的RegisterCacheEntry方法,可以這么做嗎?它不會工作!當回調(diào)發(fā)生,HttpContext已經(jīng)無法訪問。HttpContext僅僅在一個請求正在被處理的時候才可以被訪問。因為回調(diào)發(fā)生在web服務(wù)器的幕后,所以這里沒有請求需要被處理,因而HttpContext對象無法獲得。因此,你也無法從回調(diào)中訪問Cache對象。 方案是,我們需要一個簡單的請求。我們可以利用.netFramework中的WebClient類來實現(xiàn)一個對虛擬頁面的“虛擬”訪問。當虛擬頁面被執(zhí)行,我們可以Hold住HttpContext對象,然后再次注冊一個緩存項的回調(diào)。 所以,回調(diào)方法作一點修改來發(fā)出一個虛擬調(diào)用。
HitPage方法對一個虛擬頁面發(fā)出調(diào)用:
我們僅僅截獲虛擬頁面的請求,并且讓其他的頁面以他們原來的方式繼續(xù)執(zhí)行。 Web進程重啟時重啟緩存項回調(diào) 這里有很多情況,可能導(dǎo)致web服務(wù)器重啟。例如,如果系統(tǒng)管理員重啟IIS,或者重啟電腦,或者web進程陷入死循環(huán)(在windows 2003下)。在這樣的情況下,服務(wù)將停止運行,直到一個頁面被請求和Application_Start被調(diào)用。Application_Start僅僅在當一個頁面第一次被訪問時才會被調(diào)用。所以,當web進程被重啟時為了讓“服務(wù)”運行起來,我們只能手動調(diào)用“虛擬”頁面,或者某人需要訪問你站點的主頁。 一個“滑頭”的方案是:可以把搜索引擎加入你的站點中。搜索引擎時常會爬行頁面。因此,它們將訪問你站點的一個網(wǎng)頁,這就可以觸發(fā)Application_Start的執(zhí)行,因此服務(wù)將被再次啟動運行。 另一個方案是向某些通信或可用性監(jiān)控服務(wù)注冊你的站點。有許多關(guān)注你站點以及可以檢查你的站點是否正常并且性能是否良好的Web 服務(wù)。所有這些服務(wù)都需要訪問你站點的頁面然后收集統(tǒng)計信息。所以,通過注冊這樣的服務(wù),你可以保證你的站點一直“存活”著。 測試可執(zhí)行任務(wù)的類型 讓我們來測試一下,是否我們能夠做一個windowsservice能夠做的一切任務(wù)。首先,第一個問題是,我們不能做一個windows service能夠做的所有事情,因為windowsservice運行在一個本地系統(tǒng)賬戶的權(quán)限下。這是一個具有非常高權(quán)限的賬戶,使用這個賬戶你可以在你的系統(tǒng)中做任何事情。然而,asp.net web線程運行在ASPNET賬戶下(windows xp)或者NETWORKSERVICE賬戶下(windows 2003)。這是一個低權(quán)限的賬戶,并且沒有權(quán)限訪問硬盤。為了允許服務(wù)向硬盤寫東西,web進程需要被授予對文件夾的寫權(quán)限。我們都知道關(guān)于此的安全問題,所以我將不再詳述細節(jié)。 現(xiàn)在,我們將開始測試我們通常利用windowsservice完成的事情: (1) 向文件寫東西 (2) 數(shù)據(jù)庫操作 (3) Web Service調(diào)用 (4) MSMQ 操作 (5) Email 發(fā)送 讓我們來寫一些測試代碼:
測試文件“寫”操作 讓我們來測試一下是否我們真的能夠向文件內(nèi)寫東西。在C盤創(chuàng)建一個文件夾,將其命名為“temp”(如果磁盤的格式是NTFS,允許ASPNET/NETWORKSERVICE賬戶向該文件夾的寫權(quán)限)。
打開該文件,然后你應(yīng)該看到這樣的信息:
測試數(shù)據(jù)庫的可連接性 在你的“tempdb”數(shù)據(jù)庫中運行下面的代碼(也可以自己建數(shù)據(jù)庫測試)
上面的代碼將創(chuàng)建一個名為ASPNETServiceLog的表。記住,因為該表創(chuàng)建于tempdb中,所以該表在SQL Server重啟的時候?qū)⑾А?/P> 接下來,為ASPNET/NETWORKSERVICE賬戶授予tempdb數(shù)據(jù)庫的db_datawriter權(quán)限。另外,你可以定義更多特殊的權(quán)限,并且只允許往表中寫權(quán)限。 現(xiàn)在,寫下測試方法:
這將在log表中產(chǎn)生一些記錄,你可以測試來確保“服務(wù)”的執(zhí)行是否有延遲。你應(yīng)該會再每兩分鐘獲得一行數(shù)據(jù)。 ![]() 測試郵件的分發(fā) 對運行一個windows service最基本的需求是定期發(fā)送郵件提醒,狀態(tài)報告等等。所以,測試是否可以像windows service一樣發(fā)送email很重要:
請將From和To 修改為某些有效的地址,并且你應(yīng)該每兩分鐘就可以收到一次郵件提醒。 測試MSMQ 讓我們寫一個簡單的方法來測試是否我們可以從asp.net直接訪問MSMQ:
另外,你可以調(diào)用隊列的Receive方法來解析隊列中需要被處理的消息。 這里,有一個你必須記住的問題是,不要訂閱隊列的Receive事件。因為線程可能隨時會被殺死,并且web服務(wù)器可能隨時會被重啟,一個持續(xù)阻塞的Receive將不能正常地工作。另外,如果你調(diào)用BeginReceive方法同時阻塞代碼的執(zhí)行直到一個消息到達,服務(wù)將被卡住然后其他的代碼將不會再運行。所以,在這種情況下,你將不得不調(diào)用Receive方法來解析消息。 擴展系統(tǒng)功能 Asp.net服務(wù)可以被用來擴展那些可插拔的任務(wù)。你可以從web頁面中引入作業(yè)排隊,讓這種服務(wù)定期執(zhí)行。例如,你可以將作業(yè)隊列放入一個緩存項,讓“服務(wù)”來選擇任務(wù)然后執(zhí)行它。采用這種方式,你可以在你的asp.net項目中實現(xiàn)一個簡單的任務(wù)處理系統(tǒng)。 讓我們實現(xiàn)一個簡單的Job類,它包含了一個任務(wù)執(zhí)行的信息。
不要忘記鎖住靜態(tài)的“任務(wù)集合”,因為asp.net是多線程的。并且頁面會在不同的線程上執(zhí)行,所以同時往任務(wù)隊列中寫是很有可能的。 該文章在 2012/1/23 0:57:52 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |