2011-06-06 44 views
21

我有一個asp.net-mvc網站,我正在爲我的ORM使用nhibernate。在asp.net-mvc中,如何在不減緩用戶體驗的情況下運行昂貴的操作?

我有一個當前的控制器操作,執行基本的CRUD更新(查詢數據庫中的項目,然後更新一堆值並提交回數據庫表)。然後它向客戶端返回一個簡單的json響應,以指示成功或錯誤。

public ActionResult UpdateEntity(MyEntity newEntity) 
{ 
     var existingEntity = GetFromRepository(newEntity.Id); 
     UpdateExistingEntity(newEntity, existingEntity); 
     return Json(SuccessMessage); 
} 

在某些情況下(假設的承諾,如果某些字段在我的對象更改成功)我現在要引發一些額外的操作(例如發一堆人,並運行生成一個報告,一些代碼),但我不想放慢正在進行更新的用戶的用戶體驗。所以我擔心的是,如果我這樣做:

public ActionResult UpdateEntity(MyEntity newEntity) 
{ 
     var existingEntity = GetFromRepository(newEntity.Id); 
     bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity); 
     if (keyFieldsHaveChanged) 
    { 
      GenerateEmails(); 
      GenerateReports(); 
    } 
    return Json(SuccessMessage); 
} 

這對於某人更新的用戶體驗來說太慢了。無論如何(asyngc?)有一個昂貴的操作被控制器動作觸發,但沒有控制器動作因此而減慢?

+5

您可能需要考慮將該工作(電子郵件和報告)加載到單獨的服務。 – 2011-06-06 10:47:40

+0

@IKEA騷亂 - 但我將如何調用。 。生成電子郵件和報告的代碼位於網站控制器中。 。 – leora 2011-06-06 10:59:21

+3

在我工作的應用程序中,我寫了一個標誌回數據庫。一個單獨的程序,在這種情況下是一個系統服務,位於後臺,輪詢數據庫尋找工作。 – 2011-06-06 12:38:20

回答

51

我以前做過這個。

最健壯的方式是使用異步控制器,或者更好的是一個獨立的服務,如WCF服務。

但根據我的經驗,我只需要像你說的那樣做一些簡單的單線任務,比如審計或報告。

在這個例子中,最簡單的方式 - 把火關Task

public ActionResult Do() 
{ 
    SomethingImportantThatNeedsToBeSynchronous(); 

    Task.Factory.StartNew(() => 
    { 
     AuditThatTheUserShouldntCareOrWaitFor(); 
     SomeOtherOperationTheUserDoesntCareAbout(); 
    }); 

    return View(); 

} 

這是一個簡單的例子。您可以根據需要啓動儘可能多的任務,同步它們,在完成時收到通知等。

我目前使用上述功能來執行Amazon S3上傳。

+3

@ RPM1984 - 因此,爲了澄清,您不會在返回視圖之前等待任務完成。 。正確? – leora 2011-06-15 16:58:19

+0

@ooo - 正確。 – RPM1984 2011-06-15 23:39:53

+0

@ RPM1984 - 謝謝。 。我沒有意識到你能做到這一點。我認爲這可能會破壞「管道」並在某種程度上超出範圍 – leora 2011-06-16 09:21:51

5

你應該做異步操作和異步控制器來鎖定線程池,而不是讓網站的其他用戶受到影響。當任務運行很長時,從asp.net線程池取得的線程被保留,並且在操作完成之前不會返回到池。如果同時有很多長時間運行的任務,他們會預留許多線程,所以訪問您站點的其他用戶很有可能因等待而受到影響。異步操作不會使代碼更快。我建議你只使用異步控制器來處理上面提到的線程案例,但這還不夠。我認爲你應該使用一些鏈接或ajax觸發服務器上的操作,並讓用戶繼續他的網站衝浪。一旦操作完成,在下一頁刷新時,應通知用戶該任務已完成執行。這是另一個證明商業代碼不應該寫入控制器的證明。你應該爲此分開服務。

+0

感謝您的回覆。你能否澄清一下如何爲用戶返回一個值以及如何保持異步操作運行 – leora 2011-06-06 11:47:05

+0

@Paolo Moretti建議最好的方式。控制器應該很小,並且只能更新模型。比應用程序中的其他服務,可能根本不是Web應用程序--Windows服務或某個調度服務可以查看更新後的模型並執行其任務。做這種事情的方式真的太多了 - 它們取決於你的具體系統。但首先,忘記控制器操作應該執行這項工作。只要將這個過程分成觸發長時間運行的任務和執行它就可以分開。由控制器觸發。通過其他服務 – archil 2011-06-06 11:55:28

+0

執行你是否暗示這個「服務」代碼必須在我的網站之外或者僅僅是另一個程序集或類?有沒有什麼解決方案可以讓我從我的網站上運行代碼(所以我沒有管理一個單獨的獨立服務的開銷,只是爲了運行本質上只是一個昂貴的方法) – leora 2011-06-06 11:58:30

2

我認爲你確實需要一個類似於Windows Azure的工作者角色。

http://www.microsoft.com/windowsazure/features/compute/

我不知道如何最好地實現在純MVC沒有天青排隊。而且,根據你在哪裏託管(互聯網託管服務器,你自己的服務器和根等)有複雜性。

16

如果目的是立即返回JSON,並留下了緊張的工作背景,而不會影響用戶體驗的異步運行,那麼你就需要開始一個新的後臺線程。 AsyncController在這裏不會幫你。

有很多關於餓死線程請求池的爭論(這是真實的),但是counter參數是因爲服務器忙於工作,所以應該讓池停止運行。當然,理想情況下,您應該通過排隊/分佈式系統等將工作完全轉移到另一臺服務器上,但這是一個複雜的解決方案。除非你需要處理數百個請求,否則你不需要考慮這個選項,因爲它不太可能會導致問題。這實際上取決於您的解決方案的可擴展性要求,後臺進程需要多長時間以及調用的頻率。最簡單的解決方案如下:

public ActionResult UpdateEntity(MyEntity newEntity) 
{ 
    var existingEntity = GetFromRepository(newEntity.Id); 
    bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity); 
    if (keyFieldsHaveChanged) 
    { 
     ThreadPool.QueueUserWorkItem(o => 
             { 
              GenerateEmails(); 
              GenerateReports(); 
             }); 

    } 
    return Json(SuccessMessage); 
} 
1

這不是主要關於異步,因爲我正確地讀你的問題。這是關於長時間運行的操作。您應該將長時間運行的操作卸載到後臺作業中。 ASP.NET應用程序不適合執行後臺作業。我可以看到幾個選項,你可以去:

  1. Windows服務 - 這可以查詢您的數據庫的某種狀態,並能觸發從該狀態起
  2. WCF服務的行爲 - 你的ASP.NET應用程序可以發送異步請求WCF服務,而不必等待迴應
  3. 有可能是其他類似的BizTalk,但它取決於你的應用程序是如何組織等

從UI,你應該能夠輪詢定時器以向用戶提供狀態(如果您認爲t他用戶需要立即知道該狀態),或者在動作完成時向用戶發送電子郵件。

請注意,使用異步操作執行I/O非常重要,其他人已經提供了一些關於如何完成該操作的良好鏈接。

1

如果您正在考慮用戶體驗(用戶不應該等到這些任務執行完畢)和可靠性(即使應用程序重新啓動,任務也必須排隊並執行),您可以選擇使用MSMQ。

MSMQ爲assync操作提供了一個最簡單的解決方案。它支持同步操作,提供日誌和託管API,並且主要關注這種情況。

在這些文章請看:

Introduction to MSMQ

Programming MSMQ in .Net

2

這是一個老問題,所以我相信它需要更新。

我推薦使用HangFire(http://hangfire.io)。有了這個,你可以簡單地排隊你的工作,即使在網絡應用程序。 HangFire將確保工作至少運行一次。

// Static methods are for demo purposes 
BackgroundJob.Enqueue(
    () => Console.WriteLine("Simple!")); 

您還可以在良好的用戶界面中查看所有排隊作業的狀態。

+0

你是說接受的答案不再正確嗎? – leora 2015-03-26 11:04:37

+0

不,這是正確的。我的答案只是更新(更優雅,我認爲)選項,以解決在Web應用程序內長時間運行的任務:-) – 2015-03-28 11:06:39

+0

,但你必須支付使用Handfire .... – 2015-05-19 06:18:04

相關問題