Estou tentando usar o async/await
recurso ASP.NET em meu projeto de API da Web. Não tenho certeza se isso fará alguma diferença no desempenho do meu serviço Web API. Encontre abaixo o fluxo de trabalho e o código de amostra do meu aplicativo.
Fluxo de Trabalho:
Aplicativo UI → Ponto de extremidade da API da Web (controlador) → Chamar método na camada de serviço da API da Web → Chamar outro serviço da Web externo. (Aqui temos as interações DB, etc.)
Controlador:
public async Task<IHttpActionResult> GetCountries()
{
var allCountrys = await CountryDataService.ReturnAllCountries();
if (allCountrys.Success)
{
return Ok(allCountrys.Domain);
}
return InternalServerError();
}
Camada de serviço:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
return Task.FromResult(response);
}
Testei o código acima e está funcionando. Mas não tenho certeza se é o uso correto de async/await
. Por favor, compartilhe seus pensamentos.
Respostas:
Lembre-se de que o principal benefício do código assíncrono no lado do servidor é a escalabilidade . Isso não fará com que seus pedidos sejam executados com mais rapidez por mágica. Abordo várias considerações sobre "devo usar
async
" em meu artigo sobreasync
ASP.NET .Acho que seu caso de uso (chamar outras APIs) é adequado para código assíncrono, apenas tenha em mente que "assíncrono" não significa "mais rápido". A melhor abordagem é primeiro tornar sua IU responsiva e assíncrona; isso fará com que seu aplicativo pareça mais rápido, mesmo que seja um pouco mais lento.
No que diz respeito ao código, ele não é assíncrono:
Você precisaria de uma implementação verdadeiramente assíncrona para obter os benefícios de escalabilidade de
async
:Ou (se a sua lógica neste método realmente for apenas uma passagem):
Observe que é mais fácil trabalhar de "dentro para fora" do que "de fora para dentro" dessa forma. Em outras palavras, não comece com uma ação de controlador assíncrona e então force os métodos downstream a serem assíncronos. Em vez disso, identifique as operações naturalmente assíncronas (chamando APIs externas, consultas de banco de dados, etc.) e torne-as assíncronas no nível mais baixo primeiro (
Service.ProcessAsync
). Em seguida, deixe oasync
fluxo aumentar, tornando as ações do controlador assíncronas como a última etapa.E sob nenhuma circunstância você deve usar
Task.Run
neste cenário.fonte
Está correto, mas talvez não seja útil.
Como não há nada para esperar - nenhuma chamada para bloquear APIs que podem operar de forma assíncrona - então você está configurando estruturas para rastrear a operação assíncrona (que tem sobrecarga), mas não faz uso desse recurso.
Por exemplo, se a camada de serviço estava realizando operações de banco de dados com Entity Framework que oferece suporte a chamadas assíncronas:
Você permitiria que o thread de trabalho fizesse outra coisa enquanto o banco de dados era consultado (e, portanto, capaz de processar outra solicitação).
Aguardar tende a ser algo que precisa ir até o fim: é muito difícil retroceder em um sistema existente.
fonte
Você não está aproveitando async / await efetivamente porque o thread de solicitação será bloqueado durante a execução do método síncrono
ReturnAllCountries()
O encadeamento designado para lidar com uma solicitação ficará inativo esperando enquanto
ReturnAllCountries()
ele funciona.Se você puder implementar
ReturnAllCountries()
para ser assíncrono, verá benefícios de escalabilidade. Isso ocorre porque o thread pode ser liberado de volta para o pool de threads do .NET para manipular outra solicitação, enquantoReturnAllCountries()
está em execução. Isso permitiria que seu serviço tivesse um rendimento mais alto, utilizando threads com mais eficiência.fonte
Eu mudaria sua camada de serviço para:
da forma como a possui, você ainda está executando sua
_service.Process
chamada de maneira síncrona e obtendo muito pouco ou nenhum benefício em aguardar por ela.Com essa abordagem, você está agrupando a chamada potencialmente lenta em a
Task
, iniciando-a e retornando-a para ser aguardada. Agora você tem o benefício de aguardar oTask
.fonte
Service
não é um método assíncrono e não está sendo aguardado naService
própria chamada. Posso entender seu ponto, mas não acredito que se aplique aqui.Task.Run
deve ser evitado no ASP.NET. Essa abordagem removeria todos os benefícios deasync
/await
e, na verdade, teria um desempenho pior sob carga do que apenas uma chamada síncrona.