2009-09-07 45 views
3

我正在尋找一個很好的策略來真正解耦並行處理,我的web應用程序(ASP.NET MVC/C#)非直接進程。我將非直接定義爲不需要立即執行以呈現頁面或更新信息的所有內容。解耦(用於並行處理)Web應用程序的非即時處理的最佳方法?

這些過程包括髮送電子郵件,根據數據庫信息更新一些內部統計信息,從Web服務獲取外部信息,這些信息只需要定期完成,等等。

儘管主ASP.NET MVC應用程序和這些後臺任務之間需要存在一些通信;例如MVC應用程序需要通知電子郵件進程發送一些內容。

這樣做的最佳策略是什麼? MSMQ?將所有這些非直接進程轉換爲Windows服務?我正在想象一個真正解耦的場景,但我不想要一個讓問題解決/單元測試更困難或引入大量代碼的交易。

謝謝!

+0

由於我的回答從根本上離開了其他人,我誤解了你嗎?你能澄清嗎? 你是指並行執行(即多核,其他受訪者似乎假設)或卸載不是特定於當前請求的任務(在我讀的背景下運行)? –

+1

@Jed:你是對的。正如我在下面所評論的,這不是關於多核,而是關注和即時性的過程分離。 – Alex

回答

2

.NET中的ThreadPool是基於隊列的工作池,但是它在ASP.NET主機進程內部使用,所以如果嘗試更多地使用ThreadPool,可能會降低Web Server的性能。

因此,您必須創建自己的線程,將其標記爲背景並讓其每隔幾秒輪詢一次工作可用性。

做的是,在數據庫如下創建工作表的最好辦法,

Table: JobQueue 
JobID (bigint, auto number) 
JobType (sendemail,calcstats) 
JobParams (text) 
IsRunning (true/false) 
IsOver (true/false) 
LastError (text) 

JobThread類可以像下面。

class JobThread{ 
    static Thread bgThread = null; 
    static AutoResetEvent arWait = new AutoResetEvent(false); 

    public static void ProcessQueue(Job job) 
    { 
     // insert job in database 
     job.InsertInDB(); 

     // start queue if its not created or if its in wait 
     if(bgThread==null){ 
       bgThread = new Thread(new ..(WorkerProcess)); 
       bgThread.IsBackground = true; 
       bgThread.Start(); 
     } 
     else{ 
       arWait.Set(); 
     } 
    } 

    private static void WorkerProcess(object state){ 
     while(true){ 
       Job job = GetAvailableJob( 
         IsProcessing = false and IsOver = flase); 
       if(job == null){ 
        arWait.WaitOne(10*1000);// wait ten seconds. 
              // to increase performance 
              // increase wait time 
        continue; 
       } 
       job.IsRunning = true; 
       job.UpdateDB(); 
       try{ 

       // 
       //depending upon job type do something... 
       } 
       catch(Exception ex){ 
        job.LastError = ex.ToString(); // important step 
        // this will update your error in JobTable 
        // for later investigation 
        job.UpdateDB(); 
       } 
       job.IsRunning = false; 
       job.IsOver = true; 
       job.UpdateDB(); 
     } 
    } 
} 

注意 不推薦用於高內存使用率任務此實現,ASP.NET會給大量內存的大任務不可用的錯誤,例如像我們有很多圖片上傳的,我們需要創建縮略圖並使用位圖對象處理它們,ASP.NET不會允許您使用更多內存,因此我們必須創建相同類型的Windows服務。

通過創建Windows服務,您可以創建相同的線程隊列並輕鬆利用更多內存,並且可以使用WCF或Mutex對象在ASP.NET和Windows Service之間進行通信。

MSMQ MSMQ也很大,但是它增加的配置任務,並且變得難以追查,有時錯誤。我們避免MSMQ,因爲我們花費很多時間在代碼中尋找問題的答案,在MSMQ配置存在問題的地方,錯誤有時不能提供足夠的信息,說明問題的確切位置。在我們的定製解決方案中,我們可以創建具有日誌的完整調試器版本來跟蹤錯而這是Managed Programs的最大優勢,在早期的Win32應用程序中,錯誤實際上很難追蹤。

3

由於我主要在Python工作,所以不能說ASP.NET,但幸運的是我可以回答這個問題,因爲它更像是一個元語言問題。

我通常使用獨立運行的基於隊列的後端守護進行此操作。當你需要添加一些東西到隊列中時,你可以使用你選擇的方法(我偏愛HTTP)並提交一份工作。守護進程只是逐個敲打工作 - 可能將它們委派給工作線程本身。您可以出局應用程序的REST風格的側和斷火作業到後端,即:

# In frontend (sorry for Python, should be clear) 
... 
backend_do_request("http://loadbalancer:7124/ipc", my_job) 
... 

# In backend (psuedoPython) 
while 1: 
    job = wait_for_request() 
    myqueue.append(job) 
... 
def workerthread(): 
    job = myqueue.pop() 
    do_job(job) 

如果以後需要與後臺守護進程來檢查,問「是做的工作2025?」你可以在你的設計中考慮到這一點。

如果你想用Windows服務來做到這一點,我會想象你可以。所有需要做的就是聽取你選擇的端口,不管你想做什麼IPC - 我都會堅持使用網絡傳輸,因爲本地IPC將承擔同一臺機器並限制你的可擴展性。你的單元測試不應該那麼難;您可以將前端和後端視爲兩個不同的項目。

+1

+1很好的解釋。 – Alex

0

如果可以開發用於.NET 4框架然後可以通過使用F#或並行計算功能解耦(http://msdn.microsoft.com/en-us/library/dd460693(VS.100).aspx

F#被設計爲支持並行計算,以便它可能比移動代碼到一個更好的選擇服務。但是,如果你願意,你可以使用WCF並將所有東西卸載到Web服務中,但這可能並不能真正解決你的問題,因爲它只是將問題轉移到其他地方。

編輯:將非必要的移動到webservices可能是最有意義的,這是一個標準的做法,其中的web服務器是在防火牆之外,如此脆弱,所以所有的真正的工作是由其他服務器完成的,網絡服務器只負責靜態頁面和渲染。

如果您不想添加webservices,那麼您可以使用Spring.NET來完成此任務,但無論採用哪種方式,您都只需調用遠程過程來完成這項工作。

這是可擴展的,因爲您可以將業務邏輯分離到多個不同的服務器,並且由於Web服務器在很大程度上只是MVC的視圖部分,因此它可以處理比如果所有MVC工作都在Web服務器上的請求更多的請求。因爲它是爲此設計的,Spring.NET應該更容易測試,但是,webservices也可以被測試,因爲你應該分別測試每個部分,然後做功能測試,但是,通過使用Spring.NET,它是更容易模擬出關卡。

+0

我懷疑OP在談論與當前在後臺執行的請求不相關的事情,而沒有利用多核。儘管我要求澄清,也許我錯了。 –

+0

正確,這不是關於多核,而是關注和即時性的過程分離。 – Alex

0

我們已經在工作流API中完成了這項工作,或者如果它不是執行它的必要條件,您可以使用一個簡單的delegate.BeginInvoke在後臺線程上運行它。

1

在ASP.NET中處理異步處理最簡單的方法是使用ThreadPool來創建一個工作,您將工作交給您。請注意,如果您有很多小型作業正在嘗試快速切換,則默認的ThreadPool會導致一些煩人的鎖定爭用問題。在這種情況下,您需要使用C#4.0的新的竊取線程池,或者您可以使用具有竊取線程池實現的MindTouch's Dream庫(以及大量其他異步助手)並使用3.5。

0

這是一種我傾向於認爲是「離線服務」的模式,我通常將它作爲一項Windows服務實現,它可以在自己的計劃中運行多個任務。

每個任務實現一個業務流程,例如從消息隊列或數據庫表中發送掛起的電子郵件,將排隊的日誌消息寫入基礎提供者,或者執行需要定期發生的批處理,例如存檔舊數據或從傳入的Feed中導入數據對象。

此方法的優點是,您可以在任務管理服務中構建完整的管理功能,例如跟蹤,模擬,通過WCF進行遠程集成以及錯誤處理和報告,同時使用您選擇的.NET語言自己執行任務。

有一些調度API,比如Quartz.NET,可以用作這類系統的起點。就多線程而言,我的一般方法是在自己的工作線程上運行每個任務,但只允許某個任務的一個實例在給定時間運行。如果一項任務需要並行執行,那麼它將在任務主體中執行,因爲它將完全依賴於任務需要完成的工作。

我的觀點是,一個Web應用程序根本不應該管理這些類型的任務,因爲Web應用程序的目的是處理來自用戶的請求,而不是管理中間後臺作業。開始時建立這樣的系統需要很多工作,但您幾乎可以在任何項目上重新使用它。

0

使用ThreadPool管理這些任務並通過MSMQ與它通信的Windows服務無疑是我的首選方法。由於公共隊列的能力,它也具有良好的可擴展性。

1

Nservicebus聽起來像它可能適用於這裏,雖然在掩護下它可能會使用msmq。基本上,你聽起來像是在做異步的東西之後,.net有很好的處理機制。

0

MSMQ是一個很棒的方式來做到這一點。 Web場可以將請求提供給一個或多個隊列。這些隊列可以由一個或多個服務器上的一個或多個進程提供服務,從而爲您提供規模和優勢。 (如果要刪除單點故障,請在羣集上運行MSMQ)。我們在大約8 - 9年前完成了這個任務,看起來它們都運行得非常好:)甚至當時MSMQ已經很簡單了(來自COM) - 我不得不想象.NET的事情變得更好。

0

以下完善的軟件工程原理將使您的單元測試複雜性降至最低。遵循SRP(單一責任原則)。對於多線程代碼來說尤其如此,這聽起來像是你要去的地方。羅伯特馬丁在他的書「清潔代碼」中解決了這個問題。

要回答你的問題,正如你從帖子數組中看到的那樣,有許多方法可以解決後臺處理問題。 MSMQ是一種與後臺進程進行通信的好方法,也是解決可靠性問題的重要機制(例如,請求發送5封電子郵件,期望發送5封電子郵件)。

在asp.net中運行後臺進程的一種非常簡單而有效的方法是使用後臺工作器。您需要了解後臺工作者(一個線程)是否在應用程序的域或inetinfo中運行。如果它位於應用程序域中,那麼折衷是當應用程序池回收時您將失去線程。如果你需要它耐用,那麼它應該被劃分到自己的過程中(例如,Windows服務)。如果您查看WCF,Microsoft將使用MSMQ解決WS-Reliability問題。更好的消息是你可以在Windows服務中託管WCF服務。單向的服務調用足以消除Web服務器上的阻塞,從而有效地爲您提供後臺進程。

James Black提到使用Spring.NET。我同意他的建議有兩個原因:1)因爲春天。NET對服務和Web的支持優於其他框架,2)Spring.NET迫使你解耦,這也簡化了測試。

回到正軌: 1:後臺工作人員 - 權衡是與應用程序池/應用程序域密切相關,並且您沒有有效分離。適用於簡單的一次性類型作業(圖像大小調整等)。內存中的隊列是不穩定的,這可能意味着數據丟失。 2:Windows服務 - 權衡是部署的複雜性(儘管我認爲這是最小的)。如果您將擁有低資源利用率的後臺進程系列,請選擇可插拔性,並將所有主機全部集中在一個Windows服務中。對作業請求使用持久存儲(MSMQ,DB,FILE)並計劃在設計中進行恢復。如果隊列中有100個請求並且Windows服務重新啓動,則應該將其寫入,以便立即檢查隊列的工作情況。 3:在IIS中託管的WCF - 與我期望Windows服務託管WCF的複雜程度大致相同(2),這將成爲ASP.NET和服務之間的通信機制。我個人不喜歡「轉儲和運行」設計(asp.net寫入隊列),因爲它會降低清晰度,並最終與msmq緊密耦合。

相關問題