如何在ASP.NET Core程序启动时运行异步任务(3)
原文:Running async tasks on app startup in ASP.NET Core (Part 3)
作者:Andrew Lock
譯者:Lamond Lu
之前我寫了兩篇有關(guān)在ASP.NET Core中運(yùn)行異步任務(wù)的博文,本篇博文是對(duì)之前兩篇博文中演示示例和實(shí)現(xiàn)方法的簡(jiǎn)短跟進(jìn)。
你可以通過以下鏈接查看之前的博文。
如何在ASP.NET Core程序啟動(dòng)時(shí)運(yùn)行異步任務(wù)(1)
如何在ASP.NET Core程序啟動(dòng)時(shí)運(yùn)行異步任務(wù)(2)
啟動(dòng)任務(wù)的例子
在之前博客中,我收到的最常見的反饋是關(guān)于我在描述問題時(shí)使用的例子。在我最初的博客中,我列舉了3種可能場(chǎng)景,在這3種場(chǎng)景中,你希望在ASP.NET Core應(yīng)用啟動(dòng)時(shí)運(yùn)行一些異步任務(wù)。
檢查強(qiáng)類型配置是否合法
使用數(shù)據(jù)庫或者API填充緩存
運(yùn)行數(shù)據(jù)庫遷移
對(duì)于前兩種場(chǎng)景,沒有任何問題,但是對(duì)于數(shù)據(jù)庫遷移,一些博友提出了一些疑問。其實(shí)在兩篇博文中我一直都反復(fù)說明,數(shù)據(jù)庫遷移作為啟動(dòng)任務(wù)不是一個(gè)很好的方案,這里我只是想用它作為一個(gè)說明如何在ASP.NET Core程序啟動(dòng)時(shí)運(yùn)行異步任務(wù)的例子。現(xiàn)在來看,當(dāng)時(shí)使用這個(gè)例子是非常失敗的。
數(shù)據(jù)庫遷移是一個(gè)糟糕的選擇
那么為什么在ASP.NET Core應(yīng)用啟動(dòng)時(shí),運(yùn)行數(shù)據(jù)庫遷移任務(wù)會(huì)是一個(gè)問題呢?畢竟,在應(yīng)用程序開始處理請(qǐng)求之前,你肯定要完成數(shù)據(jù)庫遷移!
其實(shí)這里其實(shí)有3個(gè)問題:
數(shù)據(jù)庫遷移是應(yīng)該是單線程的
遷移數(shù)據(jù)庫需要更多的權(quán)限
開發(fā)人員不太喜歡直接運(yùn)行數(shù)據(jù)庫遷移
下面我們依次說明一下。
數(shù)據(jù)庫遷移應(yīng)該是單線程的
擴(kuò)展一個(gè)Web應(yīng)用最常用的方式之一是進(jìn)行橫向擴(kuò)展,啟動(dòng)多個(gè)運(yùn)行實(shí)例并使用負(fù)載均衡分發(fā)請(qǐng)求
這種Web集群擴(kuò)展的方案是非常有效的,特別是當(dāng)當(dāng)前應(yīng)用是無狀態(tài)的(請(qǐng)求被分發(fā)到各個(gè)應(yīng)用程序中,如果一個(gè)應(yīng)用程序崩潰,其他的Web應(yīng)用實(shí)例依然可以處理請(qǐng)求)。
但是不幸的是,如果嘗試將數(shù)據(jù)庫遷移作為應(yīng)用啟動(dòng)任務(wù),你很可能就會(huì)遇到問題。如果有超過1個(gè)以上的實(shí)例同時(shí)啟動(dòng),多個(gè)數(shù)據(jù)庫遷移任務(wù)將同時(shí)運(yùn)行。
雖然并不能保證你一定會(huì)遇到麻煩,但除非你非常小心地確保冪等更新和錯(cuò)誤處理,否則你很可能會(huì)陷入困境。
你肯定不希望使用這種方法,因?yàn)樗赡墚a(chǎn)生的數(shù)據(jù)庫完整性問題。 這里一個(gè)更好的選擇是先啟動(dòng)單個(gè)實(shí)例完成遷移操作。 這樣數(shù)據(jù)庫遷移任務(wù)變成一個(gè)單線程任務(wù),自然也就避開最嚴(yán)重的危險(xiǎn)。
這種方法比將數(shù)據(jù)庫遷移作為啟動(dòng)任務(wù)運(yùn)行更有意義,但它更安全,更容易實(shí)現(xiàn)。
當(dāng)然,這不是唯一的選擇。 如果你對(duì)啟動(dòng)任務(wù)遷移的想法有所了解,那么你可以使用分布式鎖來確保只有一個(gè)應(yīng)用程序?qū)嵗齺磉\(yùn)行遷移腳本。 然而,這并沒有解決第二個(gè)問題......
遷移通常需要更多的權(quán)限
通常來說,最佳實(shí)踐是你必須限制你的應(yīng)用程序,以便他們只有權(quán)訪問和修改所需的資源。 如果報(bào)表應(yīng)用只需要讀取銷售數(shù)據(jù),那么它應(yīng)該無法修改它們,或者更改數(shù)據(jù)庫表結(jié)構(gòu)! 為指定的連接字符串配置可操作的權(quán)限,可以防止在的的應(yīng)用出現(xiàn)安全問題時(shí)產(chǎn)生大量影響。
如果你正在使用Web應(yīng)用程序本身來運(yùn)行數(shù)據(jù)庫遷移,那么該Web應(yīng)用程序自然需要數(shù)據(jù)庫權(quán)限才能執(zhí)行高風(fēng)險(xiǎn)活動(dòng),例如修改數(shù)據(jù)庫表結(jié)構(gòu),更改權(quán)限或更新/刪除數(shù)據(jù)。 你真的希望您的Web應(yīng)用程序能夠刪除你的學(xué)生表嗎?
同樣,你可以使用一些特定的實(shí)現(xiàn),例如,與正常的數(shù)據(jù)庫訪問相比,使用不同的連接字符串進(jìn)行遷移。 但是,如果使用外部遷移過程,你根本無法鎖定Web應(yīng)用程序進(jìn)程。
開發(fā)人員不習(xí)慣直接運(yùn)行EF Core遷移
這是一個(gè)不那么明顯的觀點(diǎn),但是很多人表示在生產(chǎn)環(huán)境中使用EF Core遷移工具可能不是一個(gè)好主意。
就個(gè)人而言,我已經(jīng)有1年多沒有在生產(chǎn)環(huán)境中使用EF Core遷移了,到目前為止遷移工具肯定已經(jīng)有所改善。 話雖如此,我仍然看到一些問題:
使用EF Core全局工具進(jìn)行遷移需要安裝.NET Core SDK,這在生產(chǎn)服務(wù)器上是不需要的。
如果你想安全地更新數(shù)據(jù)庫,你可能還是必須對(duì)生成的腳本進(jìn)行一些編輯。 遷移后的數(shù)據(jù)庫結(jié)構(gòu)應(yīng)與現(xiàn)有(運(yùn)行)應(yīng)用程序兼容,以避免停機(jī)。
微軟官方文檔中暗示在應(yīng)用啟動(dòng)時(shí)運(yùn)行EF Core遷移不是一個(gè)好主意!
就我自己而言,我經(jīng)常使用DbUp和FluentMigrator,而不會(huì)使用EF Core遷移。我發(fā)現(xiàn)它們都運(yùn)行良好。
因此,如果數(shù)據(jù)庫遷移任務(wù)不適合應(yīng)用啟動(dòng)任務(wù)示例,那么哪些任務(wù)才是比較適合的呢?
比較適合作為啟動(dòng)任務(wù)的一些例子
雖然在之前的博文中我已經(jīng)反復(fù)提到了一些例子,但我還是將在下面再次描述它們。這里其他博友還給我一些有趣的補(bǔ)充。
?檢查強(qiáng)類型配置是否有效。ASP.NET Core 2.2引入了配置驗(yàn)證,但它只在首次訪問IOptions?<T>類時(shí)執(zhí)行此操作。 正如我在之前文章中所描述的那樣,你可能希望在應(yīng)用啟動(dòng)時(shí)進(jìn)行驗(yàn)證,以確保你的環(huán)境和配置有效。
填充緩存。 你的應(yīng)用程序可能需要來自文件系統(tǒng)或遠(yuǎn)程服務(wù)的數(shù)據(jù),它只需要加載一次,但加載需要耗費(fèi)相當(dāng)多的資源,所以在應(yīng)用程序啟動(dòng)之前加載此數(shù)據(jù)可減少請(qǐng)求延遲。
預(yù)連接到數(shù)據(jù)庫和/或外部服務(wù)。 以類似的方式,你可以通過連接到數(shù)據(jù)庫或其他外部服務(wù)來填充數(shù)據(jù)庫連接池。 這些通常是相對(duì)昂貴的操作,因此是很好的用例。
預(yù)編譯加載應(yīng)用中所有的單例服務(wù)。我認(rèn)為這個(gè)一個(gè)非常有趣的想法,通過預(yù)加載依賴注入容器中注冊(cè)的單例服務(wù),你可以減少請(qǐng)求的響應(yīng)時(shí)間。?
使用健康檢查來完成啟動(dòng)任務(wù)
我完全同意有關(guān)數(shù)據(jù)庫遷移的反饋。 當(dāng)這不是一個(gè)好主意時(shí),將它們用作啟動(dòng)任務(wù)的示例有點(diǎn)誤導(dǎo),特別是因?yàn)槲覀€(gè)人不使用我所描述的方法!
然而,很多人都同意我所描述的另外一種方法 - 在啟動(dòng)Kestrel服務(wù)器和處理請(qǐng)求之前運(yùn)行啟動(dòng)任務(wù)。
這里Damian Hickey針對(duì)這個(gè)方案提出了一個(gè)問題,他建議盡快啟動(dòng)Kestrel服務(wù)器。 他建議在所有啟動(dòng)任務(wù)完成后,使用運(yùn)行健康檢查向負(fù)載均衡器發(fā)出信號(hào),表明應(yīng)用程序已準(zhǔn)備好開始接收請(qǐng)求。 與此同時(shí),所有非健康檢查流量(如果負(fù)載均衡器正在執(zhí)行此任務(wù),則不應(yīng)該有任何流量)將收到503服務(wù)不可用。
這種方法的主要好處是它可以避免網(wǎng)絡(luò)超時(shí)。 一般來說,應(yīng)用程序最好能盡快的針對(duì)請(qǐng)求返回錯(cuò)誤代碼,而不是根本不響應(yīng)請(qǐng)求,并導(dǎo)致客戶端超時(shí)。 這減少了客戶端所需的資源數(shù)量。 通過較早啟動(dòng)Kestrel服務(wù)器,應(yīng)用程序可以更早地開始響應(yīng)請(qǐng)求,即使響應(yīng)是“未就緒”響應(yīng)。
這實(shí)際上與我在第一篇文章中描述的方法非常相似,但是我沒有選用它了,因?yàn)樗珡?fù)雜了,而且沒有達(dá)到我設(shè)定的目標(biāo)。 從技術(shù)上來說,在那篇文章中,我只是關(guān)注在Kestrel服務(wù)器啟動(dòng)之前運(yùn)行任務(wù)的方法,健康檢查方法不能完成這個(gè)功能。
然而,Damian提出的問題讓我再次思考。 在我下一篇博客中,我將描述如何使用ASP.NET Core 2.2的健康檢查功能向負(fù)載均衡器發(fā)送信號(hào),表明應(yīng)用程序已經(jīng)完成了所有啟動(dòng)任務(wù)。
總結(jié)
在這篇文章中,我分享了我之前關(guān)于在ASP.NET Core應(yīng)用程序啟動(dòng)時(shí)運(yùn)行異步任務(wù)的一些反饋。 這里最大的問題是我選擇使用EF Core數(shù)據(jù)庫遷移作為啟動(dòng)任務(wù)的示例。 數(shù)據(jù)庫遷移不適合在應(yīng)用程序啟動(dòng)時(shí)運(yùn)行,因?yàn)樗鼈兺ǔP枰蓡蝹€(gè)進(jìn)程運(yùn)行,并且需要比更多的數(shù)據(jù)庫權(quán)限。
我提供了一些比較適合作為的啟動(dòng)任務(wù)的場(chǎng)景,并且描述了Damian給出的建議 - 盡快啟動(dòng)Kestrel服務(wù)器,并使用運(yùn)行狀況檢查來指示任務(wù)何時(shí)完成。 我將在下一篇文章中描述如何實(shí)現(xiàn)這一功能。
總結(jié)
以上是生活随笔為你收集整理的如何在ASP.NET Core程序启动时运行异步任务(3)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows 10《描图》应用现已开源
- 下一篇: .NET Core 开源项目 Anet