Андрей Порожняков @m3ta10ph0b
.NET разработчик
Информация
- В рейтинге
- 884-й
- Откуда
- Санкт-Петербург, Санкт-Петербург и область, Россия
- Дата рождения
- Зарегистрирован
- Активность
Специализация
Backend Developer
Lead
C#
.NET
.NET Core
ASP.NET Web API
Entity Framework
Git
SQL
REST
SOLID
OOP
Ну вот при переходе на девятку или выше можно будет поэкспериментировать с отключением на уровне ASP.Net Core, чтобы не собирать лишние метрики. Спасибо за интерес к статье!
Скорее всего так: внутри ASP.Net Core собираются все метрики без исключения, а ненужные игнорируются в либе, которая метрики обрабатывает. Наверняка у вас для этих целей используется что-нибудь наподобие OpenTelemetry
Да, конечно. Часто надо посмотреть какую-то статистику наподобие "среднее время запроса к сервису" или "какой метод чаще всего вызывается". Такую статистику полезно собирать без вспомогательных технических ручек, чтобы не увидеть в ней что-то вроде такого:
Issue с дискуссией
Надеюсь, это ожидаемый результат)
Попробую помочь, но я не совсем понял, что именно происходит.
После отключения метрик, я правильно понял? И что именно началось?
Вообще по-идее отключение метрик вообще никак не должно влиять на логи, ну либо я чего-то не знаю
ХеХе, это ж как битва холодильника с обогревателем =)
Я ставлю на то, что победит IDisableHttpMetricsMetadata, т.е. метрики не соберутся.
Мой аргумент - вот эта проверка.
disableHttpRequestDurationMetric
формируется вот тут.Но лучше, конечно, подтвердить это экспериментом
Дык это ж тоже самое и есть. Те же яйца, только в профиль. Моя претензия была не в том, что описание невозможно найти, а в том, что оно мало чего описывает. После этого описания у меня вопросов осталось больше, чем ответов. Примеров использования флагов вообще не найти в майкрософтовских доках. Надеюсь, после выхода стабильной версии добавят, но надежд мало.
А вот тут соглашусь. Пока писал статью, много ковырялся в исходном коде, и мне уже проще было там смотреть. Читателю, конечно, будет удобнее смотреть в сгенеренных доках. Поправил в статье свои ссылки на ваши, спасибо за замечание.
Ну а я вот написал, тут уж извините) Посмотрел на то, какие изменения были в последних версиях - считаю, что уже можно приступать к обзорам. Каких-то глобальных изменений я не ожидаю, буду удивлён, если они будут. Есть конечно вероятность, что майкрософт всё перепилит подчистую или вообще прикроет лавочку и моя статья окажется работой в стол, но я готов такой риск принять. В ABP Framework вон уже добавили обёртку и не посмотрели, что версия предварительная. Тоже в стол ребята поработают, если что-то пойдёт не так)
Есть и я на него сослался. Я не призываю уже сейчас использовать либу Microsoft.Extensions.Caching.Hybrid, в статье есть много предупреждений на этот счёт. Более того, сейчас всё указывает на то, что стабильная версия будет проигрывать по функционалу FusionCache. Тем не менее я считаю это большим шагом для развития ASP.NET Core и говорить про такое обязательно надо.
Да, согласен, он и по функционалу пока превосходит HybridCache. Но раз майкрософтовские разработчики взяли курс на создание собственной аналогичной либы - надо однозначно за этим следить
Ну тут можно далеко пойти. Назвать все пакеты, в которых сохраняется или кешируется какая-то информация, плохими - это мощно. Как я уже сказал, это в ряде решений вообще является фичей. Где-то может быть сайд-эффектом. В любом случае, даже если принять вашу позицию, вы предлагаете детально изучать код каждого nuget-пакета перед тем, как его подключить, чтобы определить, плохой он или нет?
Могут. А ещё существуют базы данных, вы не поверите. И в них тоже можно писать информацию. Только как это всё вообще относится к тому, что написано в статье и что обсуждалось изначально? Давайте ещё раз попробую свою мысль донести: сохранение информации во внешний источник - это ок. Сохранение информации в каких-то классах, будь они статичными или нет - это тоже ок, классы для того и созданы, чтобы переносить и хранить информацию. Можно ли создавать статический класс внутри nuget-пакета? Да, технология позволяет. Не ок начинается тогда, когда облачная функция, которая должна быть stateless по определению, перестаёт быть таковой. Чтобы определить, соблюдается ли это в Яндекс Функциях, я использовал статический класс. Я не говорю, что надо везде использовать статический класс, сохранять в него информацию и потом жаловаться, что она там есть. Это искусственный пример, который в моих экспериментах является маркером того, запускается ли приложение внутри облачной функции при каждом вызове заново или мы продолжаем стрелять в поднятый ранее хост. Если можете предложить свой способ тестировать такое - предлагайте, буду рад. Никакого другого мнения вы от меня не получите, я предлагаю перестать цепляться к статическим файлам и тем более обсуждать тут хорошие/плохие практики их использования. Если хотите похоливарить по этому поводу - думаю, можно найти более подходящую по тематике публикацию и более охотливого оппонента, мне не интересно.
О, а вот тут я готов с вами согласиться и закончить этот бессмысленный флуд. Вы считаете, что отсутствие stateless на уровне архитектуры облачных функций это не проблема? Прекрасно, имеете право на своё мнение. Более того, разработчики яндекса, развивающие .NET среду для облачных функций, явно считают также. Может быть вы даже один из них. Если нет - можно попробовать получить там оффер.
Я же считаю, что память приложения, вызываемого под капотом у облачной функции, должна быть полностью очищена от информации, оставшейся от предыдущего запуска. Не должно быть такого, что логика работы приложения при холодном старте может отличаться от поведения подготовленного экземпляра. Тут не должно быть даже малейшей возможности выстрелить себе в ногу. Как этого достигать - вопрос второй. Я своё предложение описал в статье.
Нет, и я вроде бы нигде подобное не писал. Более того - для ряда приложений это было бы даже вредно. Я хочу, чтобы при каждом вызове облачной функции любая информация, оставшаяся от предыдущего вызова, была недоступна новому вызову. Именно на уровне архитектуры, а не на уровне "не используй это и это, тогда не будет". И тут странно видеть "вы хотите" - это вроде как один из основных критериев облачных функций, а не просто моя хотелка.
Тут согласен, я думаю это вполне нормально - хотеть что-то ускорить, если это возможно.
В статье я даю рабочее решение, как именно можно ускорить запуск приложений, запускаемых Яндекс Функциями. И подтверждаю его метриками. Собственно это и есть основная мысль публикации. Другое дело, что я не добавляю в свои примеры классическую работу с сервисами. Хотя я очень люблю такой подход, я не считаю его применимым для приложений, заточенных под частый запуск ради одного запроса. А именно под это и должны быть заточены приложения внутри облачных функций.
Я понимаю, к чему вы. И хочу, чтобы вы тоже поняли, к чему я. Противоречие вы видите потому, что в статье и своих комментариях я никак не оцениваю использование статических классов. Я просто указываю на тот факт, что они существуют, могут быть написаны не нами и не быть нам подконтрольными, т.е. существовать внутри nuget-пакета, например. И если такой пакет подключен, скажем, к классическому ASP.NET Core решению, то у меня нет никаких вопросов к тому, что он хранит какое-то состояние. Так как нам никто и не обещал, что ASP.NET Core на уровне архитектуры является stateless. Может там наоборот, что-то закешировать надо и это фича, а не баг.
А вот каждый новый вызов облачной функции - это, фактически, новый запуск приложения. И если в приложении осталась какая-то информация от старого запроса, это может стать для кого-то как минимум неожиданностью.
Протестировал ровно также, как тестировал описанные в статье решения для выявления тех же проблем. Или ваше решение настолько уникально, что требует какого-то отдельного подхода для его тестирования? Окей, тогда покажите, как правильно его тестировать в контексте описанной в статье проблемы.
А какую проблему тогда оно призвано решить? Я не совсем понимаю. В статье описана конкретная проблема - отсутствие штатного stateless в реализации облачных функций. Через добавление статического класса я проверяю, сохраняется их состояние или нет.
Гениально. "Чтобы не было проблемы, просто не создавайте её". Начнём с того, что я могу тоже самое воспроизвести и с созданием экземплярного класса. Второй аргумент - при подключении nuget-пакетов вы чаще всего не знаете, что там под капотом, существуют ли статические или экземплярные классы, которые могут сохранить какую-то информацию, связанную с предыдущим вызовом функции. И да - повторю свой вопрос. Цель статьи - показать проблему со stateless в реализации Яндекс Функций и предложить решение, как с этим можно побороться. Каким образом ваше решение помогает бороться с этой проблемой?
Классно. А зачем тогда держать обёртку для сервисов. Просто чтобы была и отжирала лишнее время на её компиляцию? А JIT-компиляция, напомню, происходит каждый раз при холодном старте функции. Т.е. в ряде случаев - буквально каждый раз при вызове.
Долгоживущее приложение в случае с serverless - довольно редкий кейс. Всё-таки при разработке приложения под функцию надо быть готовым, что оно будет постоянно запускаться заново для того, чтобы отработать один единственный запрос. Да, в Яндекс Функциях можно создать такую нагрузку, чтобы подготовленный экземпляр жил вечно и обслуживал запросы, не компилируя и не запуская приложение каждый раз. Но тогда вопрос - а точно ли при таком сценарии использования надо было выбирать serverless-приложение? Может достаточно было классическое приложение захостить? Тогда и работа с сервисами оправдана, и с гонкой потоков можно пободаться вашими методами.
Приветствую! Спасибо за ответ, идею понял.
На мой взгляд, добавление такой абстракции и регистрация сервисов в serverless-приложениях делает их слишком тяжеловесными. Основная идея таких приложений - быть вызванными, один раз быстро отработать и умереть до следующего вызова. Идея же регистрации сервисов внутри приложения предполагает, что приложение будет долгоживущим. В Яндекс Функциях такой вариант возможен при очень высокой нагрузке на функцию, когда вызовы происходят настолько часто, что подготовленный экземпляр существует всегда. Но в таком случае разработчик скорее всего захочет максимально оптимизировать время работы функции и будет наоборот отказываться от любых дополнительных абстракций.
В любом случае я протестировал ваше решение в Яндекс Функциях. Я добавил весь пример из https://pastebin.com/bG6DyJxn, а ещё добавил класс:
Это воспроизводит ситуацию, когда какой-то класс в нашем приложении или подключенных библиотеках сохраняет информацию из предыдущего запроса. Затем я добавил обращение к этому классу в ваш пример:
И ожидаемо получил тот же результат, что и при использовании встроенного .NET-окружения Яндекс Функций. При частых обращениях, когда вызовы обрабатывает подготовленный экземпляр, значение Counter будет накапливаться, т.е. stateless не гарантируется.
Более того, среднее время работы функции при холодном старте увеличилось до 317 ms против 216 ms при использовании встроенного .NET-окружения. И это мы ещё не зарегистрировали и не использовали ни одного сервиса, просто добавили для них обёртку.
Итог тестирования: проблема stateless для подготовленного экземпляра сохраняется, время холодного старта ухудшилось.
Но я очень благодарен за проявляемый интерес, если будут ещё варианты решения описанных проблем - готов и их протестировать и сравнить с имеющимися решениями!
Ценное замечание, спасибо!
Действительно, как минимум в некоторых других популярных решениях с .NET похожая ситуация. По крайней мере так было раньше. Я осознанно не включал это в разбор, чтобы не сводить дискуссию до уровня "Если сосед обосрался, то и мне можно". Цель публикации — подсветить конкретные проблемы с .NET на конкретной платформе. Если мои решения помогут при работе с другими платформами — так даже лучше.
А вообще было бы интересно посмотреть на сравнительный анализ запуска .NET-приложений в разных сервисах, согласен.
Признаться, яннп, как должно выглядеть ваше решение и причём тут race condition. Но если вы пришлёте работающий Hello, world — это будет действительно полезный комментарий. Уверен, многие скажут за него спасибо. Да и я тогда в статью ваше решение добавлю. С указанием авторства, разумеется. Тем более раз обёртка небольшая - не должно занять много времени
Всё именно так, и потому и приходится изобретать всякое)
Наверняка можно и так, но проблему долгого запуска при холодном старте это всё равно не решит.
А так - да, тоже одно из решений. В целом я уверен, что есть и другие решения, которые можно применить. Я описал одно из них. В чём проигрыш - не понял, ну ладно, Вам виднее.
По поводу "мы же хотим красиво" - не знаю, как по мне так было бы красиво, если бы Яндекс Функции позволяли не писать такой костыль, как Вы предложили, а поддерживали бы stateless из коробки.
Большая работа проделана и описана, спасибо!
Проблема известная (к счастью, редким "счастливчикам"). Даже открытый issue есть на эту тему, где обсуждается, в том числе, и описанное тут решение.
Оставлю ссыль, вдруг кому пригодится для отслеживания статуса https://github.com/dotnet/runtime/issues/71921
В какой-то момент его могут закрыть и проблема может стать неактуальной
Да, так можно, но если над проектом работает несколько разработчиков - в какой-то момент времени кто-то может добавить свой IStartupFilter, который будет вызван раньше, так как IStartupFilter-ы создают вложенность почти как middleware. И даже если всех предупредили, что так делать не надо - через пол года придёт новый разработчик и добавит, а на ревью не уследят. Не сказать, что большая проблема, но следить периодически придётся. Функционал, описанный в статье, сломать будет сложнее - для этого придётся явно вызвать app.UseRouting, а это уже умышленное вредительство)
Про "наследник ушедшей Nancy" - тоже вижу очевидные сходства, разработчики явно ориентировались на него и на подобные реализации в других языках.
Да, согласен, так можно сделать. Главное этот кастомный middleware с проверкой добавить в самое начало пайплайна и следить, чтобы перед ним не добавили какой-нибудь другой middleware. Я не стал в статью это всё добавлять ибо не вижу смысла писать такую обвязку, раз нам теперь из коробки всё доступно, но спасибо за замечание.