Quando devo usar controladores assíncronos no asp.net MVC?

215

Tenho algumas preocupações ao usar ações assíncronas no ASP.NET MVC. Quando é que melhorar o desempenho dos meus apps, e quando o faz não ?

  1. É bom usar ação assíncrona em qualquer lugar no ASP.NET MVC?
  2. Com relação aos métodos aguardáveis: devo usar palavras-chave assíncronas / esperadas quando quiser consultar um banco de dados (via EF / NHibernate / outro ORM)?
  3. Quantas vezes posso usar as palavras-chave aguardar para consultar o banco de dados assincronamente em um único método de ação?
Vnuuk
fonte

Respostas:

109

Os métodos de ação assíncrona são úteis quando uma ação deve executar várias operações independentes de execução longa.

Um uso típico para a classe AsyncController são as chamadas de serviço da Web de longa duração.

As chamadas do meu banco de dados devem ser assíncronas?

O pool de threads do IIS geralmente pode lidar com muito mais solicitações de bloqueio simultâneo do que um servidor de banco de dados. Se o banco de dados for o gargalo, as chamadas assíncronas não irão acelerar a resposta do banco de dados. Sem um mecanismo de limitação, o envio eficiente de mais trabalho para um servidor de banco de dados sobrecarregado usando chamadas assíncronas apenas transfere mais carga para o banco de dados. Se o seu banco de dados for o gargalo, as chamadas assíncronas não serão a bala mágica.

Você deve dar uma olhada nas referências 1 e 2

Derivado de @PanagiotisKanavos comentários:

Além disso, assíncrono não significa paralelo. A execução assíncrona libera um thread valioso do conjunto de encadeamentos do bloqueio de um recurso externo, sem custo de complexidade ou desempenho. Isso significa que a mesma máquina IIS pode lidar com solicitações mais simultâneas, não que seja executada mais rapidamente.

Você também deve considerar que as chamadas de bloqueio começam com um spinwait com uso intenso da CPU. Durante os períodos de estresse, o bloqueio de chamadas resultará em atrasos crescentes e na reciclagem do pool de aplicativos. Chamadas assíncronas simplesmente evitam isso

Tharif
fonte
27
Em relação à assíncrona, IIS e banco de dados, a postagem do blog é extremamente antiga e as conclusões tiradas aqui estão erradas. O IIS precisa armazenar uma carga muito maior que o banco de dados, os threads do pool de threads são limitados e uma longa fila de solicitações pode levar a 500. Tudo o que libera um thread enquanto aguarda a E / S é benéfico. Até os links não são sobre o que o OP pediu. Na verdade, os mesmos autores têm escrito que você deve usar métodos assíncronos DB sempre que puder
Panagiotis Kanavos
30
Além disso, assíncrono não significa paralelo. A execução assíncrona libera um thread valioso do conjunto de encadeamentos do bloqueio de um recurso externo, sem custo de complexidade ou desempenho. Isso significa que a mesma máquina IIS pode lidar com solicitações mais simultâneas, não que seja executada mais rapidamente.
Panagiotis Kanavos
5
Você também deve considerar que as chamadas de bloqueio começam com um spinwait com uso intenso da CPU. Durante os períodos de estresse, o bloqueio de chamadas resultará em atrasos crescentes e na reciclagem do pool de aplicativos. Chamadas assíncronas simplesmente evitar isso
Panagiotis Kanavos
1
Como essa é a resposta aceita e, portanto, está no topo da maioria das pessoas, pode ser aconselhável remover o segundo primeiro parágrafo sobre os tempos de execução e a execução em paralelo, o que não tem nada a ver com assíncrono. Sei que há uma nota no final, mas é bastante enganadora.
Rob
1
A execução assíncrona libera um encadeamento apenas nos casos em que é realmente assíncrono até o final da implementação e usa algum mecanismo de retorno de chamada para sinalizar a prontidão e ativar um novo encadeamento para alternar o contexto e processar o resultado. Portanto, não sei em que casos essa alternância de contexto / criação de novos threads é mais eficiente do que apenas esperar pelos resultados em um único thread? De qualquer forma, as chamadas de serviço da Web de longa duração são uma má idéia; prefiro transferi-lo para um serviço em segundo plano confiável e deixar o cliente verificar os resultados, que podem ocorrer em alguns minutos, horas ou dias.
JustAMartin
229

Você pode encontrar meu artigo do MSDN sobre o assunto útil ; Ocupei muito espaço nesse artigo, descrevendo quando você deveria usar asyncno ASP.NET, não apenas como usá async-lo.

Tenho algumas preocupações ao usar ações assíncronas no ASP.NET MVC. Quando melhora o desempenho dos meus aplicativos e quando - não.

Primeiro, entenda que async/ awaité sobre liberar threads . Nos aplicativos da GUI, trata-se principalmente de liberar o encadeamento da GUI para que a experiência do usuário seja melhor. Nos aplicativos de servidor (incluindo o ASP.NET MVC), trata-se principalmente de liberar o encadeamento de solicitação para que o servidor possa ser dimensionado.

Em particular, não irá:

  • Faça suas solicitações individuais serem concluídas mais rapidamente. Na verdade, eles serão concluídos (um pouquinho) mais devagar.
  • Retorne ao chamador / navegador quando você pressionar um await. awaitsomente "produz" para o pool de threads do ASP.NET, não para o navegador.

A primeira pergunta é - é bom usar ação assíncrona em qualquer lugar no ASP.NET MVC?

Eu diria que é bom usá-lo em qualquer lugar em que você esteja fazendo E / S. Porém, pode não ser necessariamente benéfico (veja abaixo).

No entanto, é ruim usá-lo para métodos ligados à CPU. Às vezes, os desenvolvedores acham que podem obter os benefícios asyncapenas chamando Task.Runseus controladores, e essa é uma ideia horrível. Como esse código acaba liberando o encadeamento de solicitação, retomando outro encadeamento, não há benefício algum (e, de fato, eles estão tendo a penalidade de alternar os encadeamentos extras)!

Devo usar palavras-chave assíncronas / aguardar quando quiser consultar o banco de dados (via EF / NHibernate / outro ORM)?

Você pode usar qualquer método que esteja disponível. No momento, a maioria dos principais players suporta async, mas há alguns que não. Se o seu ORM não suportar async, não tente envolvê-lo Task.Runou algo assim (veja acima).

Note que eu disse "você poderia usar". Se você está falando sobre o ASP.NET MVC com um único back-end de banco de dados, você (quase certamente) não obterá nenhum benefício de escalabilidade async. Isso ocorre porque o IIS pode manipular solicitações muito mais simultâneas do que uma única instância do SQL server (ou outro RDBMS clássico). No entanto, se o seu back-end é mais moderno - um cluster de servidor SQL, SQL Azure, NoSQL, etc - e seu backend pode escalar, e o gargalo escalabilidade é IIS, em seguida, você pode obter um benefício escalabilidade de async.

Terceira pergunta - Quantas vezes posso usar as palavras-chave aguardar para consultar o banco de dados de forma assíncrona em UM método de ação única?

Quantas você quiser. No entanto, observe que muitos ORMs têm uma regra de uma operação por conexão. Em particular, o EF permite apenas uma única operação por DbContext; isso é verdade se a operação é síncrona ou assíncrona.

Além disso, lembre-se da escalabilidade do seu back-end novamente. Se você está acessando uma única instância do SQL Server e seu IIS já é capaz de manter o SQLServer em sua capacidade total, dobrar ou triplicar a pressão no SQLServer não ajudará em nada.

Stephen Cleary
fonte
2
@ seebiscuit: Ao fazer E / S (assíncrona), nenhum thread é usado. Eu tenho um post que entra em mais detalhes.
Stephen Cleary
2
Quantas solicitações o IIS e uma única instância do SQL Server podem atender? Como posso encontrar esses números?
MOD
1
@ MOD: Depende da configuração e do hardware. A única maneira de encontrar esses números é fazer um teste de carga no seu próprio servidor.
Stephen Cleary
1
@ Flezcano: Métodos que são executados totalmente na CPU. Ou seja, eles não fazem E / S ou bloqueio.
Stephen Cleary
1
Desculpe senhor, mas ASSIM não vai aceitar essa pergunta de 2 linhas, que estou tentando entender. para servidor cada uma dessas mil solicitações ou eu deveria torná-lo assíncrono para lidar com 1000 solicitações?
Learning-Overthinker-Confused
21

é bom usar ação assíncrona em qualquer lugar no asp.net MVC?

Como de costume na programação, isso depende . Sempre há uma troca ao seguir um determinado caminho.

async-awaitbrilha em lugares em que você sabe que receberá solicitações simultâneas ao seu serviço e deseja expandir com facilidade . Como async-awaitajuda na expansão? No fato de que quando você invoca uma chamada de E / S assíncrona de forma síncrona , como uma chamada de rede ou atinge seu banco de dados, o encadeamento atual responsável pela execução é bloqueado, aguardando a conclusão da solicitação. Ao usar async-await, você habilita a estrutura para criar uma máquina de estado para você, garantindo que, após a conclusão da chamada de E / S, seu método continue sendo executado a partir de onde parou.

Um ponto a ser observado é que essa máquina de estado possui uma sobrecarga sutil. Tornar um método assíncrono não o executa mais rapidamente , e esse é um fator importante para entender e um conceito errôneo que muitas pessoas têm.

Outra coisa a ser levada em consideração ao usar async-awaité o fato de ser assíncrono o tempo todo , o que significa que você verá o assíncrono penetrar em toda a pilha de chamadas, do topo ao topo. Isso significa que, se você deseja expor APIs síncronas, geralmente se encontra duplicando uma certa quantidade de código, pois async e sync não se misturam muito bem.

Devo usar palavras-chave assíncronas / aguardar quando quiser consultar o banco de dados (via EF / NHibernate / outro ORM)?

Se você optar por seguir o caminho do uso de chamadas de E / S assíncronas, async-awaitserá uma boa opção, pois mais e mais fornecedores modernos de banco de dados expõem o método assíncrono implementando o TAP (Task Asynchronous Pattern).

Quantas vezes posso usar as palavras-chave aguardar para consultar o banco de dados de forma assíncrona em UM método de ação única?

Quantas você desejar, desde que siga as regras declaradas pelo seu provedor de banco de dados. Não há limite para a quantidade de chamadas assíncronas que você pode fazer. Se você tiver consultas independentes umas das outras e puderem ser feitas simultaneamente, poderá girar uma nova tarefa para cada uma delas e await Task.WhenAllaguardar a conclusão de ambas.

Yuval Itzchakov
fonte
2
As chamadas db assíncronas não são executadas mais rapidamente, mas permitem que a mesma máquina IIS atenda muito mais solicitações porque os threads do pool de threads não são desperdiçados. Também reduz os custos da CPU, porque o bloqueio de chamadas começa com um spinwait com uso intenso de CPU antes de realmente bloquear. Em situações de carga elevada esta pode ser a diferença entre a máquina lutando ou não e reciclagem
Panagiotis Kanavos
@ PanagiotisKanavos eu sei, isso não estava claro pelo que eu disse?
Yuval Itzchakov
1
Sua resposta não menciona as especificidades do IIS - o cenário de colapso / reciclagem é o principal motivo para o uso assíncrono. Alguém que não passou por isso provavelmente não entenderá o que scale out wellpode significar your server won't die under load. Ou pode ser um caso deonce burned ...
Panagiotis Kanavos
7

Meus 5 centavos:

  1. Use async/awaitse e somente se você fizer uma operação de E / S, como banco de dados ou serviço da web de serviço externo.
  2. Sempre prefira chamadas assíncronas ao DB.
  3. Cada vez que você consulta o banco de dados.

PS Existem casos excepcionais para o ponto 1, mas você precisa ter um bom entendimento de componentes internos assíncronos para isso.

Como uma vantagem adicional, você pode fazer algumas chamadas de E / S em paralelo, se necessário:

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
fonte
6

asyncas ações ajudam melhor quando as ações executam algumas operações de E / S no banco de dados ou em algumas chamadas vinculadas à rede, nas quais o encadeamento que processa a solicitação será interrompido antes de obter resposta da chamada vinculada ao DB ou à rede que você acabou de invocar. É melhor você aguardar com eles e isso realmente melhorará a capacidade de resposta do seu aplicativo (porque menos threads de entrada \ saída ASP ficarão paralisados ​​enquanto aguarda o banco de dados ou qualquer outra operação como essa). Em todas as minhas aplicações, sempre que muitas chamadas para o banco de dados são muito necessárias, eu sempre as envolvi no método esperado e chamei isso de awaitpalavra - chave.

Dimitri
fonte
5

Como você sabe, o MVC suporta controladores assíncronos e você deve tirar proveito disso. Caso o seu Controller execute uma operação longa (pode ser uma E / S baseada em disco ou uma chamada de rede para outro serviço remoto), se a solicitação for tratada de maneira síncrona, o encadeamento do IIS ficará ocupado o tempo todo. Como resultado, o encadeamento está apenas aguardando a conclusão da operação demorada. Pode ser melhor utilizado atendendo a outras solicitações enquanto a operação solicitada primeiro estiver em andamento. Isso ajudará a atender solicitações mais simultâneas. Seu serviço da web será altamente escalável e não será facilmente encontrado no problema do C10k . É uma boa idéia usar async / waitit para consultas de banco de dados. e sim, você pode usá-los quantas vezes quiser.

Dê uma olhada aqui para obter excelentes conselhos.

anurag
fonte
3

Minha experiência é que hoje muitos desenvolvedores usam async/await como padrão os controladores.

Minha sugestão seria: use-o somente quando souber que o ajudará .

O motivo é que, como Stephen Cleary e outros já mencionaram, ele pode apresentar problemas de desempenho, em vez de resolvê-los, e ajudará você apenas em um cenário específico:

  • Controladores de alto tráfego
  • Back-end escalável
gajama
fonte
2

É bom usar ação assíncrona em qualquer lugar no ASP.NET MVC?

É bom fazer isso onde quer que você possa usar um método assíncrono, especialmente quando tiver problemas de desempenho no nível do processo do operador, o que acontece com operações massivas de dados e cálculos. Caso contrário, não há necessidade, porque o teste de unidade precisará de fundição.

Com relação aos métodos aguardáveis: devo usar palavras-chave assíncronas / esperadas quando quiser consultar um banco de dados (via EF / NHibernate / outro ORM)?

Sim, é melhor usar async para qualquer operação de banco de dados possível para evitar problemas de desempenho no nível dos processos do trabalhador. Observe que o EF criou muitas alternativas assíncronas para a maioria das operações, como:

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

Quantas vezes posso usar as palavras-chave aguardar para consultar o banco de dados assincronamente em um único método de ação?

O céu é o limite

Shadi Namrouti
fonte
A lógica comercial para a maioria dos aplicativos da Web geralmente precisa de operações altamente seqüenciais; portanto, a alternância entre processos paralelos na maioria dos casos é viável apenas no nível mais alto do gerenciamento de solicitações da Web / processo do trabalhador, que de qualquer maneira não lida diretamente com as solicitações do banco de dados. Eu realmente gostaria de ver alguns exemplos do mundo real de um aplicativo Web típico, onde seria realmente uma boa idéia processar consultas de banco de dados de maneira assíncrona.
JustAMartin