Asp.net

Azure Web App (ASP.NET MVC) 每十分鐘變冷一次,載入需要 +10-20 秒

  • May 1, 2018

我對 Azure Web 應用程序有一個非常奇怪的問題,我對此感到非常沮喪。

我們體驗到我們的應用程序在使用時非常快速且響應迅速,但是,如果我們大約十分鐘不使用它,它的啟動非常冷(~10-20 秒)。這種冷啟動只有在涉及數據庫時才會發生。當它有點像我們發佈網路應用程序時。

我們的嘗試

使用 Azure 中的 Application Insights,我們每 5 分鐘設置一次 ping:

在此處輸入圖像描述

異常值總是由我的部署引起的(現在不使用部署槽)。然而,這個登錄頁面並沒有呼叫我們的數據庫,所以我們在這些數據中看不到“冷”啟動。

應用程序設置應該是可靠的。我們的網路應用程序託管在北歐Always on

在此處輸入圖像描述

我們只是將整個設置移動到一個新的資源組/應用服務計劃,以確保我們的問題與我們的其他應用程序糾纏在一起。新的應用服務計劃是一個Standard 1 small,這應該不是問題。看看我們的消費情況,我並不擔心,甚至可以嘗試一個較小的服務,我會在解決我們的問題後做:

在此處輸入圖像描述

我們的 SQL 數據庫也託管在北歐(檢查位置十億次,因為我以前犯過那個錯誤)。

就像應用服務一樣,我們選擇了“太大”的硬體來確保不會導致問題(標準 S0:10 個 DTU)。使用率低得離譜:

在此處輸入圖像描述

我們確實使用了持續部署(Deployment options在 Azure 菜單中),但是從部署來看,它不應該持續部署一些東西:

在此處輸入圖像描述

令人沮喪的是,該應用程序在工作時反應靈敏。當它“溫暖”時,每個頁面都會在幾秒鐘內載入,就像我的平均響應時間顯示在我們的網路應用程序上一樣:

在此處輸入圖像描述

但是當我們(或我們的使用者!)使用我們的應用程序時,這些數字完全是錯誤的。在這裡,我們體驗到第一次載入通常是 +10-20 秒。

有人有什麼主意嗎?有什麼提示嗎?你不知道我會多麼感激。

編輯和更新 1:

我決定設置更多測試。我現在已經設法通過呼叫另一個頁面來獲取顯示我們問題的真實數據。具有諷刺意味的是,這個頁面不呼叫數據庫,所以雖然我認為這是一個數據庫問題,但它看起來不像這樣。在此處查看挑戰(趨勢持續 +24 小時)。

奇怪的是,它精確到 10 秒有多穩定。而且趨勢似乎不是每 10-20 分鐘一次,而是更接近每 5 分鐘一次——它們之間的間隔完全相同:

在此處輸入圖像描述

編輯和更新 2:

我一直在探勘更多。結果發現有幾個非常有趣的見解:來自編輯 1 的“慢”11 秒呼叫僅來自美國東部和一個端點 ( <http://prntscr.com/jcv69w> ),並且

我發現的最有趣的事情如下:

應用程序本身沒有任何記憶體。我使用我假設使用一些記憶體的實體框架,但僅此而已。

我登錄了我們的應用程序,然後在 Chrome 中四處點擊。我發現,我已經訪問過的頁面是即時顯示的(使用數據庫中的數據),但是如果我打開一個新頁面,它會載入緩慢。我第一次打開頁面時似乎正在記憶體一些實體。

然後我嘗試在新瀏覽器中打開該應用程序。如果我打開之前在 Chrome 中打開的頁面,它會立即打開。如果我打開一個我之前沒有點擊過的新頁面,它將有大約 10 秒的載入時間。

我現在最好的猜測是我使用的實體框架由於某種原因出現了問題。

編輯 3:

剛剛添加了賞金,並且正在設置大量日誌記錄。我已經添加了 MiniProfiler,但無法讓它在生產中工作(僅在本地請求中顯示)。

我還添加了在 global.asax 中的日誌記錄,Application_Start並在那裡查看一些和狀態Application_BeginRequestApplication_EndRequest將很快更新調查結果。

編輯4:

所以現在我有了第一個有趣的數字。該應用程序沒有被回收。Application_Start只呼叫一次。

EndRequest我可以通過登錄和查看時差BeginRequest。我可以看到這兩個之間有多個呼叫需要超過 +15 秒……但是當網站溫暖時,它需要 ~0.5-2 秒,具體取決於頁面。所以在請求的開始和結束之間發生了一些非常奇怪的事情。進一步調試!

編輯 5:

讓 MiniProfiler 工作。以下是慢速載入的範例(約 15 秒):

在此處輸入圖像描述

我的下一步是添加實體框架跟踪,甚至添加更多線路呼叫。我在數據庫上賺錢!

編輯 6:

秋葵,我錯了。渲染方法很慢 - 而不是數據庫!我不知道如何調試這個……到Google!

在此處輸入圖像描述

編輯 7:

是時候再更新一次了。現狀是:什麼都沒有解決。

所以我嘗試了很多東西:

1)我試圖禁用所有類型的記憶體(防止在 ASP.NET MVC 中使用屬性進行特定操作的記憶體)並且我有相同的行為。第一次載入?慢。下一個負載?快速地。等待 5-10 分鐘,同樣的行為沒有解決。

  1. 我的 startup.auth 文件中有一些自定義的東西,延遲了 5 分鐘。已移除。不是問題。

3)我使用自定義屬性進行授權。我刪除了那個。

  1. 我更新了我的實體框架實現以使其在每個請求中都能正常工作

我真的很沮喪。我的下一步是:

A)嘗試製作5-10個版本的同一頁面(沒有_layout,有佈局,有數據庫,沒有數據庫,有依賴注入,沒有……所有這些東西),看看我是否能找到一個模式。

B) 嘗試將主機移至虛擬機,看看是否能解決問題

編輯 8 - 添加了新的遺物:

我現在添加了新遺物。以下是兩件非常可怕的事情(我發現並重現了錯誤!):

在此處輸入圖像描述

在前端方面(New Relic 的瀏覽器部分),兩次啟動之間有約 15 秒的間隔:

<http://prntscr.com/jevgeg>與<http://prntscr.com/jevgix>之間沒有任何區別。

我發布了一個答案,即使它沒有解決,但我 99% 確定我已經找到了潛在的問題。

發生的情況是,當我發佈時,每個視圖都需要建構。建構視圖大約需要 15 秒,這也是 New Relic 在我的最新更新中也顯示的內容。

這帶來了兩個臨時解決方案和一個更大的問題:為什麼視圖的建構如此緩慢?

臨時解決方案很簡單。要麼在發佈時編譯視圖,要麼在發布後訪問最重要的頁面。這顯然很煩人,因為我每天發布多次。

我認為它如此緩慢的原因是因為我使用了一個非常大的 Bootstrap 主題。我處理捆綁包的方式不是很有效,這可能會帶來問題。

我認為痛苦的原因是大約 10 分鐘後網站變慢了,僅僅是因為我經常發布新程式碼,並且沒有訪問我們的大部分頁面。這樣做後,它很快。

非常感謝您的幫助和支持——至少現在我可以應付了。

我有幾個可能的答案。

實體框架程式碼優先/數據庫初始化: 如果您將程式碼優先設置與遷移和可能的種子數據一起使用,則這些事情中的每一個都可能導致一些“預熱”問題。

特別是如果您沒有在應用程序啟動時初始化數據庫,這意味著您第一次訪問數據庫時就是它被初始化的時候。

實體框架版本: 實體框架本身在 5 和 6.x 中也有很多性能改進,其中一些也與冷啟動和熱啟動速度有關。

視圖未預編譯: 如果每次首次點擊新頁面(視圖)時頁面載入緩慢(例如部署後),那麼後續載入就可以了。這可能是因為沒有編譯頁面,如果是這樣,我可以詳細說明。

回收 聽起來您在應用程序回收時遇到了這些問題,並且它不是自動初始化(這就是您受到冷擊的原因)我見過的最糟糕的性能問題通常與實體框架和預編譯相關。但兩者都可以輕鬆修復。但是確保應用程序“始終執行”並在回收後自行初始化也可以確保沒有使用者受到這種冷遇。

更新: 由於它與視圖相關,我可以提供一個我發現非常有用的解決方案。安裝 RazorGenerator.Mvc Nuget 包。並且將此引擎添加為第一個引擎將確保您使用已編譯的視圖。

App_Start您可以創建一個名為的文件RazorGeneratorMvcStart.cs,其內容如下:

using RazorGenerator.Mvc;

[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(MyNamespace.RazorGeneratorMvcStart), "Start")]

namespace MyNamespace {
   public static class RazorGeneratorMvcStart {
       public static void Start() {
           ViewEngines.Engines.Insert(0, new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly));

           VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
       }
   }
}

Razor 引擎甚至可以為UsePhysicalViewsIfNewer喜歡實時替換視圖的使用者獲取參數。在這種情況下,它使用預編譯版本,除非文件夾中放置了比已編譯 .dll 更新的日期的視圖。

這種方法應該解決視圖的性能問題。

引用自:https://stackoverflow.com/questions/50115939