¿Cuándo debo usar Async Controllers en ASP.NET MVC?

215

Tengo algunas preocupaciones al usar acciones asincrónicas en ASP.NET MVC. ¿Cuándo mejora el rendimiento de mis aplicaciones y cuándo no ?

  1. ¿Es bueno usar la acción asíncrona en todas partes en ASP.NET MVC?
  2. En cuanto a los métodos que se pueden esperar: ¿debo usar palabras clave asíncronas / en espera cuando quiero consultar una base de datos (a través de EF / NHibernate / otro ORM)?
  3. ¿Cuántas veces puedo usar palabras clave en espera para consultar la base de datos de forma asincrónica en un solo método de acción?
Vnuuk
fuente

Respuestas:

109

Los métodos de acción asincrónica son útiles cuando una acción debe realizar varias operaciones independientes de larga duración.

Un uso típico de la clase AsyncController son las llamadas de servicio web de larga duración.

¿Deben las llamadas de mi base de datos ser asíncronas?

El grupo de subprocesos de IIS a menudo puede manejar muchas más solicitudes de bloqueo simultáneas que un servidor de base de datos. Si la base de datos es el cuello de botella, las llamadas asincrónicas no acelerarán la respuesta de la base de datos. Sin un mecanismo de limitación, el envío eficiente de más trabajo a un servidor de bases de datos abrumado mediante el uso de llamadas asincrónicas simplemente transfiere más carga a la base de datos. Si su DB es el cuello de botella, las llamadas asincrónicas no serán la bala mágica.

Usted debe tener una mirada en 1 y 2 referencias

Derivado de los comentarios de @PanagiotisKanavos:

Además, asíncrono no significa paralelo. La ejecución asincrónica libera un hilo valioso del conjunto de subprocesos del bloqueo de un recurso externo, sin costo de complejidad o rendimiento. Esto significa que la misma máquina IIS puede manejar más solicitudes concurrentes, no es que se ejecute más rápido.

También debe considerar que las llamadas de bloqueo comienzan con un spinwait intensivo de CPU. Durante los momentos de estrés, el bloqueo de llamadas provocará retrasos crecientes y el reciclaje del grupo de aplicaciones. Las llamadas asincrónicas simplemente evitan esto

Tharif
fuente
27
Con respecto a async, IIS y la base de datos, la publicación del blog es extremadamente antigua y las conclusiones extraídas aquí son incorrectas. IIS tiene que almacenar una carga mucho mayor que la base de datos, los subprocesos de grupo de subprocesos son limitados y una larga cola de solicitudes puede conducir a 500. Cualquier cosa que libere un subproceso mientras espera IO es beneficioso. Incluso los enlaces no son sobre lo que pidió el OP. De hecho, los mismos autores han escrito que debe usar métodos de DB asíncronos siempre que pueda
Panagiotis Kanavos
30
Además, asíncrono no significa paralelo. La ejecución asincrónica libera un hilo valioso del conjunto de subprocesos del bloqueo de un recurso externo, sin costo de complejidad o rendimiento. Esto significa que la misma máquina IIS puede manejar más solicitudes concurrentes, no es que se ejecute más rápido.
Panagiotis Kanavos
55
También debe considerar que las llamadas de bloqueo comienzan con un spinwait intensivo de CPU. Durante los momentos de estrés, el bloqueo de llamadas provocará retrasos crecientes y el reciclaje del grupo de aplicaciones. Las llamadas asincrónicas simplemente evitan esto
Panagiotis Kanavos
1
Dado que esta es la respuesta aceptada y, por lo tanto, en la parte superior para la mayoría de las personas, sería aconsejable eliminar el segundo primer párrafo con respecto a los tiempos de ejecución y la ejecución en paralelo, lo que no tiene nada que ver con asíncrono. Me doy cuenta de que hay una nota al final, pero es bastante engañosa.
Rob
1
La ejecución asincrónica libera un subproceso solo en los casos en que es realmente asíncrono hasta el final de la implementación y utiliza algún mecanismo de devolución de llamada para indicar la preparación y girar un nuevo subproceso para cambiar el contexto y procesar el resultado. Entonces, no estoy seguro de en qué casos este cambio de contexto / creación de un nuevo hilo es más eficiente que solo esperar los resultados en un solo hilo. Las llamadas de servicio web de larga ejecución son una mala idea de todos modos, prefiero descargarlo a un servicio en segundo plano confiable y dejar que el cliente verifique los resultados, que pueden llegar en unos minutos, horas o días.
JustAMartin
229

Puede encontrar útil mi artículo de MSDN sobre el tema ; Tomé mucho espacio en ese artículo que describe cuándo debe usar asyncASP.NET, no solo cómo usar asyncASP.NET.

Tengo algunas preocupaciones al usar acciones asincrónicas en ASP.NET MVC. Cuando mejora el rendimiento de mis aplicaciones y cuándo no.

Primero, comprenda que async/ awaitse trata de liberar hilos . En las aplicaciones GUI, se trata principalmente de liberar el hilo GUI para que la experiencia del usuario sea mejor. En aplicaciones de servidor (incluido ASP.NET MVC), se trata principalmente de liberar el hilo de solicitud para que el servidor pueda escalar.

En particular, no:

  • Haga que sus solicitudes individuales se completen más rápido. De hecho, se completarán (solo un poquito) más lentamente.
  • Regrese a la persona que llama / al navegador cuando golpea un await. awaitsolo "cede" al grupo de subprocesos ASP.NET, no al navegador.

La primera pregunta es: ¿es bueno usar la acción asíncrona en todas partes en ASP.NET MVC?

Yo diría que es bueno usarlo donde sea que esté haciendo E / S. Sin embargo, puede no ser necesariamente beneficioso (ver más abajo).

Sin embargo, es malo usarlo para métodos vinculados a la CPU. A veces, los desarrolladores piensan que pueden obtener los beneficios asyncsimplemente llamando Task.Runa sus controladores, y esta es una idea horrible. Debido a que ese código termina liberando el subproceso de solicitud al tomar otro subproceso, por lo que no hay ningún beneficio en absoluto (y de hecho, ¡están tomando la penalidad de los cambios de subproceso adicionales!

¿Debo usar palabras clave asíncronas / en espera cuando deseo consultar la base de datos (a través de EF / NHibernate / otro ORM)?

Podrías usar cualquier método que tengas disponible. En este momento, la mayoría de los jugadores principales son compatibles async, pero hay algunos que no. Si su ORM no es compatible async, no intente envolverloTask.Run ni nada por el estilo (ver arriba).

Tenga en cuenta que dije " podría usar". Si está hablando de ASP.NET MVC con un único back-end de base de datos, entonces (casi seguro) no obtendrá ningún beneficio de escalabilidad async. Esto se debe a que IIS puede manejar muchas más solicitudes concurrentes que una sola instancia del servidor SQL (u otro RDBMS clásico). Sin embargo, si su back-end es más moderno (un clúster de servidor SQL, Azure SQL, NoSQL, etc.) y su back-end puede escalar y su cuello de botella de escalabilidad es IIS, entonces puede obtener un beneficio de escalabilidad async.

Tercera pregunta: ¿cuántas veces puedo usar palabras clave en espera para consultar la base de datos de forma asincrónica en UN método de acción único?

Tantos como quieras. Sin embargo, tenga en cuenta que muchos ORM tienen una regla de una operación por conexión. En particular, EF solo permite una sola operación por DbContext; Esto es cierto si la operación es síncrona o asíncrona.

Además, tenga en cuenta la escalabilidad de su back-end nuevamente. Si está llegando a una sola instancia de SQL Server y su IIS ya es capaz de mantener SQLServer a plena capacidad, duplicar o triplicar la presión sobre SQLServer no lo ayudará en absoluto.

Stephen Cleary
fuente
2
@seebiscuit: Al realizar E / S (asíncrona correcta), no se utilizan subprocesos. Tengo una publicación de blog que entra en más detalles.
Stephen Cleary
2
¿Cuántas solicitudes pueden manejar IIS y una única instancia de SQL Server? ¿Cómo puedo encontrar estos números?
MOD
1
@MOD: depende de la configuración y el hardware. La única forma de encontrar estos números es hacer una prueba de carga en su propio servidor.
Stephen Cleary
1
@Flezcano: Métodos que se ejecutan completamente en la CPU. Es decir, no hacen E / S ni bloquean.
Stephen Cleary
1
Lo siento, señor, pero NO aceptaré esta pregunta de 2 líneas que estoy tratando de entender. Cuando estaba investigando para esto, como si tuviera 1 punto final de API web que es llamado por 1000 usuarios al mismo tiempo, entonces este punto final podría al servidor de cada una de estas mil solicitudes o debería hacerlo asíncrono para manejar 1000 de la solicitud
Learning-Overthinker-Confused
21

¿Es bueno usar la acción asíncrona en todas partes en ASP.NET MVC?

Como es habitual en la programación, depende . Siempre hay una compensación cuando se va por un camino determinado.

async-awaitbrilla en lugares donde sabe que recibirá solicitudes simultáneas para su servicio y desea poder escalar bien. ¿Cómo async-awaitayuda con la reducción de escala? En el hecho de que cuando invocas una llamada IO asíncrona sincrónicamente , como una llamada de red o golpear tu base de datos, el hilo actual que es responsable de la ejecución se bloquea esperando que finalice la solicitud. Cuando usasasync-await , habilita el marco para crear una máquina de estado para usted que se asegura de que después de que se complete la llamada IO, su método continúe ejecutándose desde donde lo dejó.

Una cosa a tener en cuenta es que esta máquina de estado tiene una sobrecarga sutil. Hacer un método asincrónico no hace que se ejecute más rápido , y ese es un factor importante para comprender y un error que muchas personas tienen.

Otra cosa a tener en cuenta cuando se usa async-awaites el hecho de que es asíncrono todo el tiempo , lo que significa que verá asíncrono penetrar en toda su pila de llamadas, de arriba a abajo. Esto significa que si desea exponer API sincrónicas, que a menudo se encontrará la duplicación de una cierta cantidad de código, como asíncrono y sincronización no se mezclan muy bien.

¿Debo usar palabras clave asíncronas / en espera cuando deseo consultar la base de datos (a través de EF / NHibernate / otro ORM)?

Si elige seguir el camino del uso de llamadas IO asíncronas, entonces sí, async-awaitserá una buena opción, ya que cada vez más proveedores de bases de datos modernos exponen el método asíncrono que implementa el TAP (Patrón Asíncrono de Tarea).

¿Cuántas veces puedo usar palabras clave en espera para consultar la base de datos de forma asincrónica en UN método de acción único?

Todos los que desee, siempre y cuando siga las reglas establecidas por su proveedor de base de datos. No hay límite para la cantidad de llamadas asíncronas que puede hacer. Si tiene consultas que son independientes entre sí y se pueden hacer simultáneamente, puede girar una nueva tarea para cada una y usarla await Task.WhenAllpara esperar a que se completen ambas.

Yuval Itzchakov
fuente
2
Las llamadas db asincrónicas no se ejecutan más rápido, pero permiten que la misma máquina IIS atienda muchas más solicitudes porque los subprocesos de threadpool no se desperdician. También reduce los costos de CPU porque las llamadas de bloqueo en realidad comienzan con un spinwait intensivo de CPU antes de realmente bloquear. En situaciones de alta carga, esta puede ser la diferencia entre la máquina que lucha o falla y recicla
Panagiotis Kanavos
@PanagiotisKanavos Lo sé, ¿no estaba claro por lo que dije?
Yuval Itzchakov 01 de
1
Su respuesta no menciona los detalles de IIS: el escenario de fusión / reciclaje es una razón principal para usar asíncrono en todo momento. Alguien que no lo haya experimentado probablemente no entenderá eso scale out wellpuede significar your server won't die under load. O puede ser un caso deonce burned ...
Panagiotis Kanavos
7

Mis 5 centavos:

  1. Use async/awaitsi y solo si realiza una operación de E / S, como DB o servicio web de servicio externo.
  2. Siempre prefiera las llamadas asíncronas a DB.
  3. Cada vez que consulta la base de datos.

PD: Hay casos excepcionales para el punto 1, pero debe tener una buena comprensión de los componentes internos asíncronos para esto.

Como ventaja adicional, puede hacer pocas llamadas IO en paralelo si es necesario:

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

asynclas acciones ayudan mejor cuando las acciones realizan algunas operaciones de E / S a la base de datos o algunas llamadas enlazadas a la red donde el subproceso que procesa la solicitud se detendrá antes de que reciba la respuesta de la DB o la llamada enlazada a la red que acaba de invocar. Es mejor que use esperar con ellos y realmente mejorará la capacidad de respuesta de su aplicación (porque menos hilos de entrada / salida ASP se detendrán mientras espera la base de datos o cualquier otra operación como esa). En todas mis aplicaciones cada vez que son necesarias muchas llamadas a la base de datos, siempre las he incluido en un método aceptable y lo llamé con una awaitpalabra clave.

Dimitri
fuente
5

Como sabes, MVC admite controladores asíncronos y deberías aprovecharlo. En caso de que su controlador realice una operación prolongada (puede ser una E / S basada en disco o una llamada de red a otro servicio remoto), si la solicitud se maneja de manera síncrona, el subproceso IIS está ocupado todo el tiempo. Como resultado, el hilo solo está esperando que se complete la larga operación. Se puede utilizar mejor atendiendo otras solicitudes mientras la operación solicitada en primer lugar está en progreso. Esto ayudará a atender más solicitudes concurrentes. Su servicio web será altamente escalable y no se encontrará fácilmente con el problema C10k . Es una buena idea usar async / await para consultas db. y sí, puede usarlos tantas veces como lo considere conveniente.

Eche un vistazo aquí para obtener excelentes consejos.

anurag
fuente
3

Mi experiencia es que hoy muchos desarrolladores usan async/await por defecto los controladores.

Mi sugerencia sería, úsela solo cuando sepa que le ayudará .

La razón es que, como Stephen Cleary y otros ya mencionaron, puede introducir problemas de rendimiento, en lugar de resolverlos, y lo ayudará solo en un escenario específico:

  • Controladores de alto tráfico
  • Backend escalable
gajama
fuente
2

¿Es bueno usar la acción asíncrona en todas partes en ASP.NET MVC?

Es bueno hacerlo siempre que pueda utilizar un método asíncrono, especialmente cuando tiene problemas de rendimiento en el nivel de proceso de trabajo que ocurre para datos masivos y operaciones de cálculo. De lo contrario, no es necesario porque las pruebas unitarias necesitarán fundición.

En cuanto a los métodos que se pueden esperar: ¿debo usar palabras clave asíncronas / en espera cuando quiero consultar una base de datos (a través de EF / NHibernate / otro ORM)?

Sí, es mejor usar asíncrono para cualquier operación de base de datos como sea posible para evitar problemas de rendimiento a nivel de procesos de trabajo. Tenga en cuenta que EF ha creado muchas alternativas asíncronas para la mayoría de las operaciones, como:

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

¿Cuántas veces puedo usar palabras clave en espera para consultar la base de datos de forma asincrónica en un solo método de acción?

El cielo es el limite

Shadi Namrouti
fuente
La lógica empresarial para la mayoría de las aplicaciones web generalmente necesita operaciones altamente secuenciales, por lo que saltar entre procesos paralelos en la mayoría de los casos es viable solo en el nivel más alto de la solicitud web / gestión de procesos de trabajo, que de todos modos no se ocupa directamente de las solicitudes de la base de datos. Realmente me gustaría ver algunos ejemplos del mundo real de una aplicación web típica en la que sería una buena idea procesar consultas DB de forma asíncrona.
JustAMartin