C# 5 .NET MVC 長非同步任務、進度報告和全域取消
我使用 ASP.Net MVC 5 並且我有一個長時間執行的操作,它必須輪詢 Web 服務、處理數據並將它們儲存在數據庫中。
為此,我想使用 TPL 庫來非同步啟動任務。
但我想知道如何做三件事:
- 我想報告此任務的進度。為此,我考慮 SignalR
- 我希望能夠離開我開始執行此任務的頁面,並能夠報告整個網站的進度(從左側的面板,但這沒關係)
- 我希望能夠全域取消這個任務(從我左邊的面板)
我對所涉及的所有技術都了解不少。但我不確定實現這一目標的最佳方法。
有人可以幫助我了解最佳解決方案嗎?
您想要執行長時間執行的工作,而使用者可以離開啟動工作的頁面,這意味著您需要“在後台”執行這項工作。它不能作為正常 HTTP 請求的一部分執行,因為使用者可能隨時通過導航或關閉瀏覽器來取消他的請求。事實上,這對你來說似乎是一個關鍵場景。
ASP.NET 中的後台工作很危險。你當然可以成功,但要做到正確並不容易。此外,工作程序可能因多種原因退出(應用程序池回收、部署、機器重啟、機器故障、堆棧溢出或無關執行緒上的 OOM 異常)。因此,請確保您的長期工作能夠容忍中途中斷。您可以減少發生這種情況的可能性,但永遠不要排除這種可能性。
您可以通過將所有工作包裝在事務中來使您的程式碼在任意終止時安全。當然,這僅在您不引起非事務性副作用(例如更改狀態的 Web 服務呼叫)時才有效。在這裡不可能給出一個一般性的答案,因為在存在任意終止的情況下實現安全性在很大程度上取決於要完成的具體工作。
這是我過去使用過的一種可能的架構:
- 當有工作進入時,您將所有必要的輸入數據寫入數據庫表並向客戶報告成功。
- 您需要一種方法來啟動工人從事該工作。您可以為此立即啟動一項任務。您還需要定期檢查以查找未啟動的工作,以防應用在添加工作項後但在為其啟動任務之前退出。讓 Windows 任務計劃程序每分鐘呼叫一次應用程序中的秘密 URL,以執行此操作。
- 當您開始從事一項工作時,您將該工作標記為正在執行,這樣就不會意外地再次拾取它。完成這項工作,寫下結果並將其標記為完成。全部在一次交易中。當您的程序恰好中途退出時,數據庫將重置所有涉及的數據。
- 將作業進度寫入單獨連接和單獨事務上的單獨表行。瀏覽器可以輪詢伺服器以獲取進度資訊。您也可以使用 SignalR,但我沒有這方面的經驗,我預計在任意終止的情況下很難讓它恢復進度報告。
- 取消將通過在進度資訊行中設置取消標誌來完成。該應用程序需要輪詢該標誌。
也許您可以使用消息隊列來處理作業,但我總是很謹慎地使用它。要以事務處理方式處理消息,您需要 MSDTC,許多 SQL Server 的高可用性解決方案都不支持它。
您可能認為這種架構不是很複雜。它利用輪詢來處理很多事情。輪詢是一種原始技術,但效果很好。它可靠且易於理解。它有一個簡單的並發模型。
如果您可以假設您的應用程序永遠不會在不合時宜的時間退出,那麼架構會簡單得多。但這不能假設。您不能假設在工作時間內不會進行部署,並且不會存在導致崩潰的錯誤。