c# - mvc - web api async controller
Utilice efectivamente async/await con API web ASP.NET (4)
No estoy muy seguro de si habrá alguna diferencia en el rendimiento de mi API.
Tenga en cuenta que el beneficio principal del código asincrónico en el lado del servidor es la
escalabilidad
.
No hará que sus solicitudes se ejecuten mágicamente más rápido.
Cubro varias consideraciones sobre "debería usar
async
" en mi
artículo sobre ASP.NET
async
.
Creo que su caso de uso (llamar a otras API) es adecuado para el código asincrónico, solo tenga en cuenta que "asincrónico" no significa "más rápido". El mejor enfoque es primero hacer que su IU responda y sea asíncrona; esto hará que tu aplicación se sienta más rápida incluso si es un poco más lenta.
En lo que respecta al código, esto no es asíncrono:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
return Task.FromResult(response);
}
Necesitaría una implementación verdaderamente asincrónica para obtener los beneficios de escalabilidad de
async
:
public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}
O (si su lógica en este método realmente es solo un paso):
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}
Tenga en cuenta que es más fácil trabajar desde "adentro hacia afuera" que desde "afuera hacia adentro" de esta manera.
En otras palabras, no comience con una acción de controlador asíncrono y luego fuerce los métodos posteriores para que sean asíncronos.
En su lugar, identifique las operaciones naturalmente asíncronas (llamadas a API externas, consultas de bases de datos, etc.), y haga que sean asíncronas en el nivel
más
bajo primero (
Service.ProcessAsync
).
Luego, deje que se
async
la
async
, haciendo que las acciones de su controlador sean asíncronas como el último paso.
Y bajo ninguna circunstancia debe usar
Task.Run
en este escenario.
Estoy tratando de hacer uso de la función
async/await
de ASP.NET en mi proyecto de API web.
No estoy muy seguro de si habrá alguna diferencia en el rendimiento de mi servicio de API web.
A continuación encontrará el flujo de trabajo y el código de muestra de mi aplicación.
Flujo de trabajo:
Aplicación de IU → Punto final de API web (controlador) → Método de llamada en la capa de servicio de API web → Llamar a otro servicio web externo. (Aquí tenemos las interacciones DB, etc.)
Controlador:
public async Task<IHttpActionResult> GetCountries()
{
var allCountrys = await CountryDataService.ReturnAllCountries();
if (allCountrys.Success)
{
return Ok(allCountrys.Domain);
}
return InternalServerError();
}
Capa de servicio:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
return Task.FromResult(response);
}
Probé el código anterior y está funcionando.
Pero no estoy seguro de si es el uso correcto de
async/await
.
Por favor comparte tus pensamientos.
Cambiaría su capa de servicio a:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
return Task.Run(() =>
{
return _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}
}
tal como lo tiene, todavía está ejecutando su
_service.Process
call sincrónicamente, y obtiene muy poco o ningún beneficio de esperarlo.
Con este enfoque, está envolviendo la llamada potencialmente lenta en una
Task
, iniciándola y devolviéndola para que la espere.
Ahora obtienes el beneficio de esperar la
Task
.
Es correcto, pero quizás no sea útil.
Como no hay nada que esperar, no hay llamadas a las API de bloqueo que podrían funcionar de forma asíncrona, entonces está configurando estructuras para rastrear la operación asincrónica (que tiene sobrecarga) pero no está haciendo uso de esa capacidad.
Por ejemplo, si la capa de servicio realizaba operaciones de base de datos con Entity Framework que admite llamadas asincrónicas:
public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
using (db = myDBContext.Get()) {
var list = await db.Countries.Where(condition).ToListAsync();
return list;
}
}
Permitiría que el subproceso de trabajo haga otra cosa mientras se consulta la base de datos (y, por lo tanto, puede procesar otra solicitud).
Aguardar tiende a ser algo que tiene que llegar hasta el final: es muy difícil volver a encajar en un sistema existente.
No está aprovechando async / await efectivamente porque el hilo de solicitud se bloqueará mientras se ejecuta el método
sincrónico
ReturnAllCountries()
El subproceso que se asigna para manejar una solicitud estará inactivo esperando mientras
ReturnAllCountries()
hace su trabajo.
Si puede implementar
ReturnAllCountries()
para que sea asíncrono, verá beneficios de escalabilidad.
Esto se debe a que el subproceso podría devolverse al grupo de subprocesos .NET para manejar otra solicitud, mientras se ejecuta
ReturnAllCountries()
.
Esto permitiría que su servicio tenga un mayor rendimiento al utilizar subprocesos de manera más eficiente.