Веб-приложение Azure (ASP.NET MVC) становится холодным каждые десять минут и занимает +10-20 секунд для загрузки


У меня очень странная проблема с веб-приложением Azure, и я очень разочарован этим.

Мы испытываем, что наше приложение очень быстро и отзывчиво при его использовании, однако, если мы не используем его в течение примерно десяти минут, оно имеет очень холодный старт (~10-20 секунд). Этот холодный запуск происходит только тогда, когда он включает базу данных. Когда это немного похоже на то, когда мы выпускаем веб-приложение.

Наши попытки

Используя Application Insights в Azure, мы настроили этот пинг каждые 5 минут:

Введите описание изображения здесь

Выбросы всегда вызваны моими развертываниями (не используя слоты развертывания прямо сейчас). Однако эта страница входа не вызывает нашу базу данных, поэтому мы не видим "холодного" начала в этих данных.

Настройка приложения должна быть надежной. Наше веб-приложение размещено в Северной Европе с Always on:

Введите описание изображения здесь

Мы просто перенесли всю настройку на новый план обслуживания группы ресурсов / приложений, чтобы убедиться, что наша проблема была запутался с другими нашими приложениями. Новый план обслуживания приложений - это Standard 1 small, что не должно быть проблемой. Глядя на наше потребление, я не беспокоюсь, и, вероятно, мог бы даже попробовать меньшую услугу, которую я сделаю после решения нашей проблемы:

Введите описание изображения здесь

Наша база данных SQL также размещена в Северной Европе (проверил местоположения миллиард раз, потому что я уже делал эту ошибку раньше).

Как и в случае с сервисом приложений, мы выбрали" слишком большое " оборудование, чтобы убедиться, что это так. не вызывая проблемы (стандартный S0: 10 DTU). Использование смехотворно низкое:

Введите описание изображения здесь

Мы используем непрерывное развертывание (Deployment options внутри меню Azure), но, глядя на развертывания, он не должен постоянно развертывать что-то:

Введите описание изображения здесь

Разочарование приходит в приложение супер отзывчивым, когда оно работает. Когда он "теплый", каждая страница загружается в считанные секунды, как показывает мое среднее время отклика в нашем интернете приложение:

Введите описание изображения здесь

Но эти цифры просто неверны, когда мы (или наши пользователи!) используйте наше приложение. Здесь мы испытываем это очень часто +10-20 секунд нагрузки в первый раз. У кого-нибудь есть какие-нибудь идеи? Какие-нибудь намеки? Ты даже не представляешь, как я буду тебе благодарна.

РЕДАКТИРОВАТЬ И ОБНОВЛЯТЬ 1:

Я решил провести еще несколько тестов. Теперь мне удалось получить реальные данные, показывающие нашу проблему, позвонив на другую страницу. По иронии судьбы эту страницу не называют база данных, так что, хотя я думал, что это была проблема базы данных, это не похоже на это. Смотрите вызов здесь (тренд продолжается +24 часа).

Странно, насколько он стабилен ровно ~10 секунд. Причем тенденция вроде бы идет не каждые 10-20 минут, а ближе к каждой 5 минуте - с точно таким же интервалом между ними:

Введите описание изображения здесь

РЕДАКТИРОВАТЬ И ОБНОВЛЯТЬ 2:

Я тут еще кое-что раскопал. Оказывается есть парочка очень интересных insights:" медленные " 11-секундные вызовы из edit 1, только с востока США и с одной конечной точки (http://prntscr.com/jcv69w ), и

Самое интересное, что я нашел, это следующее:

Само приложение не имеет никакого кэширования. Я использую Entity Framework, которая, как я предполагаю, использует некоторое кэширование, но это все.

Я вошел в наше приложение и щелкнул по нему в Chrome. Я обнаружил, что страницы, которые я уже посетил, показывали мгновенный (с данными из DB), но если я открою новую страницу, она будет загружаться медленно. Казалось, что некоторые сущности кэшируются при первом открытии страницы.

Затем я попытался открыть приложение в новом браузере. Если бы я открыл страницу, которую ранее открывал в Chrome, она открылась бы мгновенно. Если бы я открыл новую страницу, на которую не нажимал раньше, она бы загружалась ~10 секунд.

Сейчас я лучше всего предполагаю, что структура сущностей, которую я использую, по какой-то причине создает проблемы.

Править 3:

Просто добавил щедрость,и настраивает много журналов. Я добавил Минипрофайлер, но мне трудно заставить его работать в производстве (показывается только на местных запросах).

Я также добавил logging in global.asax для Application_Start и Application_BeginRequest и Application_EndRequest, чтобы увидеть некоторые и статус там. Скоро будет обновлено с выводами.

Править 4:

Итак, теперь у меня есть первые интересные цифры. Приложение не перерабатывается. Application_Start вызывается только один раз.

Я вижу время. разница путем входа в систему EndRequest и BeginRequest. Я вижу, что есть несколько вызовов, которые занимают более +15 секунд между этими двумя... Но когда сайт теплый, это занимает ~0,5-2 секунды в зависимости от страницы. Так что между началом и концом запроса происходит что-то очень странное. Отладка дальше!

Править 5:

Получил MiniProfiler для работы. Вот пример медленной нагрузки (~15 секунд):

Введите описание изображения здесь

Мой следующий шаг-добавление сущности Отслеживание фреймворка и даже некоторые дополнительные линии для линейных вызовов. Я получаю свои деньги на базе данных!

Править 6:

Окидоки, я был неправ. это медленный метод визуализации, а не база данных! Я понятия не имею, как это отладить... в google!

Введите описание изображения здесь

Править 7:

Время для еще одного обновления. Статус такой: ничего не решено.

Итак, я перепробовал много вещей:

1) я пытался отключить все типы кэширования (запретить кэширование в ASP.NET MVC для конкретных действий с использованием атрибута ) и у меня такое же поведение. Первый груз? Медленный. Следующий груз? Быстро. Подождите 5-10 минут, такое же поведение так и не решается.

2) у меня были некоторые пользовательские вещи в моем стартапе.файл auth с 5-минутной задержкой. Удаленный. Но это не проблема. 3) я использовал пользовательский атрибут для авторизации. Я убрал это.

4) я обновил свою реализацию Entity Framework, чтобы она работала в per запрос

Я действительно расстраиваюсь. Мой следующий шаг:

А) попробуйте сделать 5-10 версий одной и той же страницы (без _layout, с макетом, с базой данных, без базы данных, с внедрением зависимостей, без... все эти вещи), так что посмотрим, смогу ли я найти образец.

B) попробуйте переместить хостинг на виртуальную машину, чтобы посмотреть, решит ли это проблему

EDIT 8-ДОБАВЛЕНА НОВАЯ РЕЛИКВИЯ:

Теперь я добавил новую реликвию. Две очень страшные вещи заключаются в следующем (я нашел и воспроизвел ошибку!):

Введите описание изображения здесь

И frontend wise (браузерная часть New Relic), есть недостаток ~15s между двумя запусками:

Http://prntscr.com/jevgeg vs http://prntscr.com/jevgix между ними ничего нет.

6 16

6 ответов:

У меня есть несколько возможных ответов.

Entity framework code-first / инициализация базы данных: Если вы используете code first setup с миграциями и, возможно, начальными данными, каждая из этих вещей может вызвать некоторые проблемы "прогрева".

Особенно если вы не инициализируете базу данных при запуске приложения, это будет означать, что первый раз вы попали в базу данных, когда она инициализируется.

Версия фреймворка сущности: Entity framework сама по себе также имела много улучшения производительности в 5 и 6.x некоторые из них также имеют отношение как к холодным, так и к теплым скоростям запуска.

Представления не являются предварительно скомпилированными: Если вы получаете медленную загрузку страниц (например, после развертывания) при каждом первом посещении новой страницы (просмотр), а затем последующие загрузки будут в порядке. Это может быть потому, что страницы не компилируются, я могу подробно остановиться на этом, если это так.

Recycle Похоже, что вы испытываете эти проблемы, когда приложение повторяет, и это разве автоматическая инициализация (именно поэтому вы получаете этот холодный удар) худшие проблемы производительности, которые я видел с этими вещами, обычно связаны с entity framework и precompile. Но и то и другое можно легко исправить. Но обеспечение того, что приложение "всегда работает" и самоинициализируется после рециркуляции, также гарантирует, что пользователи не получат этого холодного удара.

Обновление: Поскольку это было связано с видом, я могу предложить решение, которое я нашел очень полезным. Установка 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 была помещена в папку.

Этот подход должен решить проблемы производительности с представлениями.

Несколько идей:

  1. В колонке веб-приложения перейдите в меню диагностика и решение проблем. Затем нажмите на счетчики производительности. Я бы честно прошерстил каждый из доступных счетчиков perf, обращая внимание на временную шкалу против вашей ухудшенной производительности. Однажды я узнал, что SignalR душит мой сервер из-за неуправляемых соединений, посмотрев на счетчик потоков.

  2. Является ли журнал ошибок сервера в Application Insights чистым?

  3. Под днищем Экран диагностики и решения проблем, вы видите что-нибудь подозрительное в журналах трассировки неудачных запросов?

  1. Отделите что-либо от развертывания против локального. Если приложение работает идеально в локальной среде, то при переходе в Azure происходит что-то другое. Разрешение чего-то требует много времени.
  2. любой статический ресурс (скрипт, стили и т. д.) автоматически кэшируется браузером по первому запросу, поэтому при последующем запросе проблема не должна возникать.
  3. Поскольку вы знаете, что метод "рендеринга" дает проблему, выглядит как сложное вычисление вложенности и браузер Происходит манипуляция домами. Однако если это не проблема, когда на локальном, то проверьте, если рендеринг вызовов для внешних ресурсов и они получают блокирующие вызовы (возможно, потребуется интенсивное использование ajax или асинхронных вызовов на стороне сервера, хотя я предполагаю, что у вас уже есть это на месте.)
  4. может потребоваться возможный рефакторинг (чтобы иметь меньшие стеки и возможное использование потоковых / неблокирующих операций ввода-вывода), однако требуется увидеть ваш код, чтобы предложить это.

Поделитесь своим событием загрузки страницы и любые последующие звонки, сделанные им. Кроме того, как вид отображается вместе с любой обработкой DOM.

Есть еще два варианта, которые нужно найти и исправить.

1. пользователь Новая реликвия для отслеживания.

Проверьте Этот пост от Хансельмана на использование его в Azure.

2. следуйте Asp.Net лучшие практики MVC

Проверьтеэтот пост на предмет лучших практик.

Также для чтения данных вы можете использовать ADO.NET с хранимыми процедурами вместо Entity Framework, потому что на данный момент EF может иметь некоторые проблемы с производительностью.

Я публикую ответ, хотя он не решен, но я на 99% уверен, что нашел основную проблему.

Что происходит, когда я освобождаю, каждый вид должен быть построен. Это занимает ~15 секунд, чтобы построить представление, которое является то, что New Relic также показывает в моем последнем обновлении.

Это приводит к двум временным решениям и одному более важному вопросу: почему построение представления происходит так медленно?

Временные решения просты. Либо компилируйте представления при выпуске, либо посетите самые важные страницы сразу после выпуска. Это, очевидно, раздражает, потому что я выпускаю несколько раз в день.

Причина, по которой это так медленно, я предполагаю, потому что я использую очень большую тему Bootstrap. Способ, которым я управляю пакетами, не очень эффективен, что может создать проблему.

Причина, по которой я думал, что боль была в том, что сайт был медленным после ~10 минут, была просто потому, что я выпускал новый код довольно часто и не посещал большую часть наших страниц. После выполнения это, это быстро.

Большое спасибо за вашу помощь и поддержку - по крайней мере, теперь я могу справиться с этим.

Вы компилируете Less или Sass в вашей установке связывания?
Если да, то какой JavaScriptEngineSwitcher вы используете?
Я помню, что у меня была похожая проблема. Пакеты компилируются при первом доступе, и это занимает огромное количество времени.
Решение состояло в том, чтобы переключиться на двигатель V8.