Когда я должен использовать асинхронные контроллеры в ASP.NET MVC?

216

У меня есть некоторые проблемы с использованием асинхронных действий в ASP.NET MVC. Когда это улучшить производительность своих приложений, а когда делает это не ?

  1. Хорошо ли использовать асинхронное действие везде в ASP.NET MVC?
  2. Относительно ожидаемых методов: использовать ли ключевые слова async / await, когда я хочу сделать запрос к базе данных (через EF / NHibernate / другой ORM)?
  3. Сколько раз я могу использовать ключевые слова await для асинхронного запроса к базе данных одним единственным действием?
Vnuuk
источник

Ответы:

109

Методы асинхронного действия полезны, когда действие должно выполнять несколько независимых длительных операций.

Типичное использование класса AsyncController - длительные вызовы веб-службы.

Должны ли мои вызовы базы данных быть асинхронными?

Пул потоков IIS часто может обрабатывать гораздо больше одновременных запросов на блокировку, чем сервер базы данных. Если база данных является узким местом, асинхронные вызовы не будут ускорять ответ базы данных. Без механизма регулирования эффективная передача дополнительной работы перегруженному серверу базы данных с помощью асинхронных вызовов просто переносит большую часть нагрузки на базу данных. Если ваша БД является узким местом, асинхронные вызовы не будут волшебной пулей.

Вы должны взглянуть на 1 и 2 ссылки

Получено из комментариев @PanagiotisKanavos:

Более того, асинхронность не означает параллель. Асинхронное выполнение освобождает ценный поток пула потоков от блокировки для внешнего ресурса, без каких-либо сложностей или затрат производительности. Это означает, что одна и та же машина IIS может обрабатывать больше одновременных запросов, но не будет работать быстрее.

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

Tharif
источник
27
Что касается асинхронности, IIS и базы данных, пост в блоге очень старый, и сделанные здесь выводы неверны. IIS должен обслуживать гораздо большую нагрузку, чем база данных, потоки пула потоков ограничены, а длинная очередь запросов может привести к 500. Все, что освобождает поток во время ожидания ввода-вывода, является полезным. Даже ссылки не о том, что спросил ОП. Фактически, те же авторы написали, что вы должны использовать асинхронные методы БД везде, где можете
Panagiotis Kanavos
30
Более того, асинхронность не означает параллель. Асинхронное выполнение освобождает ценный поток пула потоков от блокировки для внешнего ресурса, без каких-либо сложностей или затрат производительности. Это означает, что одна и та же машина IIS может обрабатывать больше одновременных запросов, но не будет работать быстрее.
Панагиотис Канавос
5
Вы также должны учитывать, что блокирующие вызовы начинаются с интенсивной загрузки процессора. Во время стресса блокировка вызовов приведет к увеличению задержек и повторному использованию пула приложений. Асинхронные вызовы просто избегают этого
Panagiotis Kanavos
1
Поскольку это общепринятый ответ и, следовательно, на вершине для большинства людей, было бы разумно удалить второй первый абзац, касающийся времени выполнения и параллельной работы, что не имеет ничего общего с асинхронностью. Я понимаю , что есть примечание в конце, но это совершенно вводит в заблуждение.
Роб
1
Асинхронное выполнение освобождает поток только в тех случаях, когда он действительно асинхронен до самого конца реализации и использует некоторый механизм обратного вызова, чтобы сигнализировать о готовности и ускорить новый поток, чтобы переключить контекст и обработать результат. Итак, я не уверен, в каких случаях это переключение контекста / создание нового потока более эффективно, чем просто ожидание результатов в одном потоке? В любом случае, длительные вызовы веб-службы - плохая идея, я предпочитаю переложить их на надежную фоновую службу и позволить клиенту проверять результаты, которые могут прийти через несколько минут, часов или дней.
JustAMartin
229

Вы можете найти мою статью MSDN на эту тему полезной ; В этой статье я занял много места, описывая, когда вы должны использовать asyncв ASP.NET, а не только как использовать asyncв ASP.NET.

У меня есть некоторые проблемы с использованием асинхронных действий в ASP.NET MVC. Когда это улучшает производительность моих приложений, а когда - нет.

Во-первых, понять, что async/ awaitэто все о освобождении темы . В приложениях с графическим интерфейсом главное - освободить поток графического интерфейса, чтобы пользовательский интерфейс стал лучше. В серверных приложениях (включая ASP.NET MVC) речь идет главным образом об освобождении потока запросов, чтобы сервер мог масштабироваться.

В частности, он не будет:

  • Сделайте ваши индивидуальные запросы быстрее. На самом деле, они будут заканчиваться (чуть-чуть) медленнее.
  • Вернитесь к вызывающему / браузеру, когда вы нажмете await. awaitтолько «уступает» пулу потоков ASP.NET, а не браузеру.

Первый вопрос - хорошо ли использовать асинхронное действие везде в ASP.NET MVC?

Я бы сказал, что это хорошо использовать везде, где вы делаете ввод / вывод. Это не обязательно может быть полезным , хотя (см. Ниже).

Тем не менее, это плохо использовать его для методов, связанных с процессором. Иногда разработчики думают, что они могут получить выгоду async, просто вызывая Task.Runсвоих контроллеров, и это ужасная идея. Поскольку этот код в конечном итоге освобождает поток запроса, занимая другой поток, так что никакой выгоды нет вообще (и на самом деле они берут штраф за дополнительные переключатели потока)!

Должен ли я использовать ключевые слова async / await при запросе к базе данных (через EF / NHibernate / другой ORM)?

Вы можете использовать любые доступные вам методы. Сейчас большинство основных игроков поддерживают async, но есть некоторые, которые этого не делают. Если ваш ORM не поддерживает async, не пытайтесь обернуть егоTask.Run или что- роде (см. Выше).

Обратите внимание, что я сказал: «Вы можете использовать». Если вы говорите о ASP.NET MVC с одним бэкэндом базы данных, то вы (почти наверняка) не получите никакой выгоды от масштабируемости async. Это связано с тем, что IIS может обрабатывать гораздо больше одновременных запросов, чем один экземпляр SQL-сервера (или другой классической RDBMS). Однако, если ваш Бэкэнд более современный - это SQL - сервер кластер, Azure SQL, NoSQL, и т.д. - и ваш бэкенд могут масштабировать, и ваша масштабируемость узкого IIS, то вы можете получить выгоду от масштабируемости async.

Третий вопрос. Сколько раз я могу использовать ключевые слова await для асинхронного запроса к базе данных в ОДНОМ методе одиночного действия?

Столько, сколько вам нравится. Однако обратите внимание, что многие ORM имеют правило одной операции на соединение. В частности, EF допускает только одну операцию для DbContext; это верно, является ли операция синхронной или асинхронной.

Кроме того, помните о масштабируемости вашего бэкэнда снова. Если вы работаете с одним экземпляром SQL Server, и ваш IIS уже способен поддерживать SQLServer на полную мощность, то удвоение или утроение нагрузки на SQLServer вам совсем не поможет.

Стивен Клири
источник
2
@seebiscuit: при выполнении (правильного асинхронного) ввода / вывода потоки не используются. У меня есть запись в блоге, которая более подробно.
Стивен Клири
2
Сколько запросов может обрабатывать IIS и один экземпляр SQL Server? Как я могу найти эти цифры?
MOD
1
@MOD: это зависит от конфигурации и оборудования. Единственный способ найти эти цифры - провести нагрузочный тест на вашем собственном сервере.
Стивен Клири
1
@Flezcano: методы, которые полностью выполняются на процессоре. Т.е. они не делают ввод / вывод или блокировку.
Стивен Клири
1
Извините, сэр, но ТАК не приму этот 2-х линейный вопрос, который я пытаюсь понять. Когда я исследовал эту вещь, например, если у меня есть 1 конечная точка веб-API, которая вызывается 1000 пользователями одновременно, тогда эта конечная точка сможет на сервер каждый из этой тысячи запросов или я должен сделать его асинхронным для обработки 1000 запросов?
Обучение-Overthinker-Confused
21

это хорошо использовать асинхронное действие везде в ASP.NET MVC?

Как обычно в программировании, это зависит . Всегда есть компромисс при движении по определенному пути.

async-awaitсветит в тех местах, где, как вы знаете, вы будете получать параллельные запросы к вашему сервису, и вы хотите иметь возможность хорошо масштабироваться . Как async-awaitпомочь с масштабированием? Дело в том, что при синхронном вызове асинхронного вызова ввода-вывода , такого как сетевой вызов или обращение к базе данных, текущий поток, отвечающий за выполнение, блокируется в ожидании завершения запроса. При использовании async-awaitвы разрешаете платформе создавать конечный автомат для вас, который гарантирует, что после завершения вызова IO ваш метод продолжит выполняться с того места, где он остановился.

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

Еще одна вещь, которую следует учитывать при использовании, async-awaitэто тот факт, что он полностью асинхронный , а это означает, что вы увидите асинхронное проникновение во весь стек вызовов, сверху донизу. Это означает, что если вы хотите использовать синхронные API-интерфейсы, вам часто приходится дублировать определенное количество кода, поскольку асинхронизация и синхронизация не очень хорошо сочетаются.

Должен ли я использовать ключевые слова async / await при запросе к базе данных (через EF / NHibernate / другой ORM)?

Если вы решите пойти по пути использования асинхронных вызовов ввода-вывода, тогда да, async-awaitбудет хорошим выбором, так как все больше и больше современных поставщиков баз данных предоставляют асинхронный метод, реализующий TAP (Task Asynchronous Pattern).

Сколько раз я могу использовать ключевые слова await для асинхронного запроса к базе данных в ОДНОМ методе одиночного действия?

Столько, сколько вы хотите, если вы следуете правилам, установленным вашим поставщиком базы данных. Количество асинхронных звонков, которые вы можете сделать, не ограничено. Если у вас есть запросы, которые не зависят друг от друга и могут быть выполнены одновременно, вы можете раскрутить новое задание для каждого и использовать, await Task.WhenAllчтобы дождаться завершения обоих.

Ювал Ицчаков
источник
2
Асинхронные вызовы БД не выполняются быстрее, но позволяют одной и той же машине IIS обслуживать гораздо больше запросов, поскольку потоки пула потоков не теряются. Это также снижает стоимость процессора, потому что блокирование вызовов на самом деле начинается с интенсивной загрузки процессора, прежде чем фактически блокируется. В ситуациях с высокой нагрузкой это может быть разницей между неисправностью или
отказом
@PanagiotisKanavos Я знаю, это было неясно из того, что я сказал?
Ювал Ицчаков
1
В вашем ответе не упоминаются особенности IIS - сценарий расплавления / повторного использования - основная причина повсеместного использования асинхронности. Кто-то, кто не испытал это, вероятно, не поймет, что scale out wellможет означать your server won't die under load. Или это может быть случайonce burned ...
Panagiotis Kanavos
7

Мои 5 центов:

  1. Используйте, async/awaitесли и только если вы выполняете операцию ввода-вывода, например, БД или веб-сервис внешней службы.
  2. Всегда предпочитайте асинхронные вызовы БД.
  3. Каждый раз, когда вы запрашиваете БД.

PS Для пункта 1 есть исключительные случаи, но для этого вам нужно хорошо понимать асинхронные внутренние компоненты.

В качестве дополнительного преимущества вы можете сделать несколько параллельных вызовов IO, если это необходимо:

Task task1 = FooAsync(); // launch it, but don't wait for result
Task task2 = BarAsync(); // launch bar; now both foo and bar are running
await Task.WhenAll(task1, task2); // this is better in regard to exception handling
// use task1.Result, task2.Result
bohdan_trotsenko
источник
6

asyncдействия помогают лучше всего, когда действия выполняют некоторые операции ввода-вывода для БД или некоторые сетевые вызовы, где поток, который обрабатывает запрос, будет остановлен, прежде чем он получит ответ от БД или сетевого связанного вызова, который вы только что вызвали. Лучше всего использовать await с ними, и это действительно улучшит скорость отклика вашего приложения (потому что меньше потоков ввода / вывода ASP будут остановлены во время ожидания БД или любой другой подобной операции). Во всех моих приложениях, когда много обращений к БД очень необходимо, я всегда заключал их в метод awaiatable и вызывал это с помощью awaitключевого слова.

Dimitri
источник
5

Как вы знаете, MVC поддерживает асинхронные контроллеры, и вы должны воспользоваться этим. Если ваш контроллер выполняет длительную операцию (это может быть дисковый ввод-вывод или сетевой вызов другой удаленной службе), если запрос обрабатывается синхронно, поток IIS все время занят. В результате поток просто ожидает завершения длительной операции. Его можно лучше использовать, обслуживая другие запросы, пока выполняется операция, запрошенная в первую очередь. Это поможет в обслуживании большего количества одновременных запросов. Ваш веб-сервис будет хорошо масштабируемым и не сможет легко столкнуться с проблемой C10k . Рекомендуется использовать async / await для запросов к базе данных. и да, вы можете использовать их столько раз, сколько считаете нужным.

Посмотрите здесь для отличного совета.

Анураг
источник
3

По моему опыту, сегодня многие разработчики async/awaitпо умолчанию используют контроллеры.

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

Причина в том, что, как уже упоминали Стивен Клири и другие, он может создавать проблемы с производительностью, а не решать их, и он поможет вам только в конкретном сценарии:

  • Контроллеры с высоким трафиком
  • Масштабируемый бэкэнд
gajama
источник
2

Хорошо ли использовать асинхронное действие везде в ASP.NET MVC?

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

Относительно ожидаемых методов: использовать ли ключевые слова async / await, когда я хочу сделать запрос к базе данных (через EF / NHibernate / другой ORM)?

Да, лучше использовать async для любой операции с БД, чтобы избежать проблем с производительностью на уровне рабочих процессов. Обратите внимание, что EF создал много асинхронных альтернатив для большинства операций, таких как:

.ToListAsync()
.FirstOrDefaultAsync()
.SaveChangesAsync()
.FindAsync()

Сколько раз я могу использовать ключевые слова await для асинхронного запроса к базе данных одним единственным действием?

Небо это предел

Шади Намроути
источник
Бизнес-логика для большинства веб-приложений обычно требует очень последовательных операций, поэтому переход между параллельными процессами в большинстве случаев жизнеспособен только на самом верхнем уровне управления веб-запросами / рабочими процессами, который в любом случае напрямую не обрабатывает запросы к базе данных. Мне бы очень хотелось увидеть некоторые реальные примеры типичного веб-приложения, где было бы неплохо обрабатывать запросы к БД асинхронно.
JustAMartin