Exportar (0) Imprimir
Expandir todo
Este artículo se tradujo de forma manual. Mueva el puntero sobre las frases del artículo para ver el texto original.
Traducción
Original

Usar un controlador asincrónico en ASP.NET MVC

La clase AsyncController permite escribir métodos de acción asincrónicos. Los métodos de acción asincrónicos se pueden utilizar para solicitudes de ejecución prolongada no relacionadas con la CPU. De este modo, el servidor web no se bloquea y se puede realizar trabajo en él mientras se procesa la solicitud. Un uso típico de la clase AsyncController es la realización de llamadas a servicios Web de ejecución prolongada.

Este tema contiene las siguientes secciones:

Hay un proyecto de Visual Studio con código fuente disponible para este tema: Descargar.

En el servidor web, .NET Framework mantiene un grupo de subprocesos que se utilizan para dar servicio a las solicitudes de ASP.NET. Cuando se recibe una solicitud, se envía un subproceso del grupo para procesarla. Si la solicitud se procesa de manera sincrónica, el subproceso se bloquea mientras la procesa, de modo que ese subproceso no puede prestar servicio a otra solicitud.

Esto podría no dar lugar a ningún problema, porque se puede crear un grupo de subprocesos lo bastante grande para alojar numerosos subprocesos bloqueados. Sin embargo, el número de subprocesos del grupo de subprocesos está limitado. En las aplicaciones grandes que procesan simultáneamente varias solicitudes de ejecución prolongada, podrían bloquearse todos los subprocesos disponibles. Esta situación se denomina colapso de los subprocesos. Cuando se llega a esta situación, el servidor web pone las solicitudes en cola. Si la cola de solicitudes se llena, el servidor web rechaza las solicitudes y muestra el estado HTTP 503 (Server Too Busy) (servidor ocupado).

En las aplicaciones susceptibles de que se produzca el colapso de los subprocesos, se pueden configurar las acciones para que se procesen de forma asincrónica. Una solicitud asincrónica tarda el mismo tiempo en procesarse que una sincrónica. Por ejemplo, si una solicitud realiza una llamada de red que tarda dos segundos en completarse, la solicitud tardará dos segundos con independencia de que se procese de manera sincrónica o asincrónica. Sin embargo, durante una llamada asincrónica, no se bloquea la capacidad del servidor de responder a otras solicitudes mientras espera a que se complete la primera. Por consiguiente, las solicitudes asincrónicas evitan que se pongan en cola las solicitudes cuando hay muchas de ellas que invocan operaciones de ejecución prolongada.

Cuando se invoca una acción asincrónica, se llevan a cabo los siguientes pasos:

  1. El servidor web recibe un subproceso del grupo de subprocesos (el subproceso de trabajo) y lo programa para que controle una solicitud entrante. Este subproceso de trabajo inicia una operación asincrónica.

  2. El subproceso de trabajo se devuelve al grupo de subprocesos para prestar servicio a otra solicitud web.

  3. Cuando la operación asincrónica se completa, se lo notifica a ASP.NET.

  4. El servidor web recibe un subproceso de trabajo del grupo de subprocesos (que podría ser diferente del subproceso que inició la operación asincrónica) para procesar el resto de la solicitud, lo que incluye presentar la respuesta.

En la siguiente ilustración se muestra el modelo asincrónico.

Canalización asincrónica

En esta sección se muestran las directrices para decidir cuándo utilizar métodos de acción sincrónicos o asincrónicos. Se trata de meras directrices; debe estudiar individualmente cada aplicación para determinar si los métodos de acción asincrónicos ayudan a mejorar el rendimiento.

En general, utilice canalizaciones sincrónicas cuando se cumplan las siguientes condiciones:

  • Las operaciones son simples o de ejecución breve.

  • La simplicidad es más importante que la eficacia.

  • Las operaciones son principalmente operaciones de la CPU y no operaciones que requieren una gran sobrecarga del disco o de la red. El uso de métodos de acción asincrónicos en operaciones relacionadas con la CPU no proporciona ninguna ventaja y da lugar a mayor sobrecarga.

En general, utilice canalizaciones asincrónicas cuando se cumplan las siguientes condiciones:

  • Las operaciones están relacionadas con la red o con E/S y no con la CPU.

  • Las pruebas muestran que las operaciones que causan bloqueos constituyen un cuello de botella para el rendimiento del sitio y que IIS puede prestar servicio a más solicitudes si se utilizan métodos de acción asincrónicos para estas llamadas que causan bloqueos.

  • El paralelismo es más importante que la simplicidad de código.

  • Se desea proporcionar un mecanismo que permita al usuario cancelar solicitudes de ejecución prolongada.

En el ejemplo descargable se muestra cómo utilizar con eficacia los métodos de acción asincrónicos. En el ejemplo de programa se llama al método Sleep para simular un proceso de ejecución prolongada. Pocas aplicaciones de producción muestran ventajas tan evidentes del uso de métodos de acción asincrónicos.

Se deben probar las aplicaciones para determinar si los métodos asincrónicos proporcionan ventajas de rendimiento o no. En algunos casos, podría ser más conveniente aumentar el número máximo de solicitudes simultáneas de IIS por CPU y el número máximo de subprocesos simultáneos por CPU. Para obtener más información sobre la configuración de subprocesos de ASP.NET, vea la entrada ASP.NET Thread Usage on IIS 7.0 and 6.0 del blog de Thomas Marquardt. Para obtener más información acerca de cuándo realizar las llamadas asincrónicas a la base de datos, vea la entrada Should my database calls be Asynchronous? del blog de Rick Anderson.

Pocas aplicaciones exigen que todos los métodos de acción sean asincrónicos. Con frecuencia, basta con convertir algunos métodos de acción sincrónicos en métodos asincrónicos para obtener la máxima eficacia para la cantidad de trabajo requerida.

En el siguiente ejemplo de código se muestra un método de acción sincrónico que se utiliza para mostrar los elementos de noticias de un controlador de portal. La solicitud Portal/News?city=Seattle muestra las noticias correspondientes a Seattle.

public class PortalController: Controller {
    public ActionResult News(string city) {
        NewsService newsService = new NewsService();
        ViewStringModel headlines = newsService.GetHeadlines(city);
        return View(headlines);
    }
}

En el siguiente ejemplo se muestra el método de acción News reescrito como método asincrónico.

public class PortalController : AsyncController {
    public void NewsAsync(string city) {

        AsyncManager.OutstandingOperations.Increment();
        NewsService newsService = new NewsService();
        newsService.GetHeadlinesCompleted += (sender, e) =>
        {
            AsyncManager.Parameters["headlines"] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };
        newsService.GetHeadlinesAsync(city);
    }

    public ActionResult NewsCompleted(string[] headlines) {
        return View("News", new ViewStringModel
        {
            NewsHeadlines = headlines
        });
    }
}

Para convertir un método de acción sincrónico en uno asincrónico, son necesarios los siguientes pasos:

  1. En lugar de derivar el controlador de Controller, derívelo de AsyncController. Los controladores que se derivan de AsyncController permiten a ASP.NET procesar solicitudes asincrónicas, aunque también pueden prestar servicio a métodos de acción sincrónicos.

  2. Cree dos métodos para la acción. El método que inicia el proceso asincrónico debe tener un nombre compuesto de la acción y del sufijo "Async". El método que se invoca cuando finaliza el proceso asincrónico (el método de devolución de llamada) debe tener un nombre compuesto de la acción y del sufijo "Completed". En el ejemplo anterior, el método News se ha convertido en dos métodos: NewsAsync y NewsCompleted.

    El método NewsAsync devuelve void (ningún valor en Visual Basic). El método NewsCompleted devuelve una instancia de ActionResult. Si bien la acción consta de dos métodos, se tiene acceso a ella mediante la misma dirección URL que para un método de acción sincrónico (por ejemplo, Portal/News?city=Seattle). Métodos como RedirectToAction y RenderAction también harán referencia al método de acción como News y no como NewsAsync.

    Los parámetros que se pasan a NewsAsync utilizan los mecanismos normales de enlace de parámetros. Los parámetros que se pasan a NewsCompleted utilizan el diccionario Parameters.

  3. Reemplace la llamada sincrónica del método ActionResult original por una llamada asincrónica en el método de acción asincrónico. En el ejemplo anterior, la llamada a newsService.GetHeadlines se reemplaza por una llamada a newsService.GetHeadlinesAsync.

La clase NewsService que el método NewsAsync utiliza es un ejemplo de un servicio que expone métodos mediante un modelo asincrónico basado en eventos. Para obtener más información sobre este modelo, vea Event-based Asynchronous Pattern Overview.

La propiedad OutstandingOperations notifica a ASP.NET cuántas operaciones están pendientes. Esto es necesario porque ASP.NET no puede determinar cuántas operaciones ha iniciado el método de acción ni cuándo se han completado esas operaciones. Cuando la propiedad OutstandingOperations es cero, ASP.NET completa la operación asincrónica total llamando al método NewsCompleted.

Tenga en cuenta lo siguiente acerca de los métodos de acción asincrónicos:

  • Si el nombre de acción es Sample, el marco buscará los métodos SampleCompleted y SampleAsync.

  • Las páginas de vista deben denominarse Sample.aspx y no SampleAsync.aspx o SampleCompleted.aspx. (El nombre de acción es Sample, no SampleAsync.)

  • Un controlador no puede contener un método asincrónico denominado SampleAsync y un método sincrónico denominado Sample. Si lo contiene, se inicia una excepción AmbiguousMatchException porque los métodos de acción SampleAsync y Sample tienen la misma firma de la solicitud.

Los métodos de acción asincrónicos resultan útiles cuando una acción debe realizar varias operaciones independientes. Por ejemplo, un sitio de portal podría no limitarse a mostrar noticias, sino incluir también información sobre deportes, el tiempo, mercados de valores, etc.

En el siguiente ejemplo se muestra una versión sincrónica del método de acción Index del portal de noticias.

public ActionResult IndexSynchronous( string city ) {
    
    NewsService newsService = new NewsService();
    string[] headlines = newsService.GetHeadlines();

    SportsService sportsService = new SportsService();
    string[] scores = sportsService.GetScores();

    WeatherService weatherService = new WeatherService();
    string[] forecast = weatherService.GetForecast();

    return View("Common", new PortalViewModel  {
        NewsHeadlines = headlines,
        SportsScores = scores,
        Weather = forecast
    });
}

Las llamadas a cada servicio se realizan secuencialmente. Por consiguiente, el tiempo que se necesita para responder a la solicitud es la suma de cada llamada a un servicio más una pequeña cantidad de sobrecarga. Por ejemplo, si las llamadas tardan 400, 500 y 600 milisegundos, el tiempo de respuesta total será ligeramente superior a 1,5 segundos. Sin embargo, si las llamadas a los servicios se realizan de forma asincrónica (en paralelo), el tiempo de respuesta total será ligeramente superior a 600 milisegundos, porque esa es la duración de la tarea más larga.

En el siguiente ejemplo se muestra una versión asincrónica del método de acción Index del portal de noticias.

public void IndexAsync(string city) {
    AsyncManager.OutstandingOperations.Increment(3);

    NewsService newsService = new NewsService();
    newsService.GetHeadlinesCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["headlines"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    newsService.GetHeadlinesAsync();

    SportsService sportsService = new SportsService();
    sportsService.GetScoresCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["scores"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    sportsService.GetScoresAsync();

    WeatherService weatherService = new WeatherService();
    weatherService.GetForecastCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["forecast"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    weatherService.GetForecastAsync();
}

public ActionResult IndexCompleted(string[] headlines, string[] scores, string[] forecast) {
    return View("Common", new PortalViewModel  {
        NewsHeadlines = headlines,
        SportsScores = scores,
        Weather = forecast
    });
}          
}

En el ejemplo anterior, se llama al método Increment con un parámetro de 3 porque hay tres operaciones asincrónicas.

Si desea aplicar atributos a un método de acción asincrónico, aplíquelos al método ActionAsync en lugar de al método ActionCompleted. Los atributos del método AcciónCompleted se omiten.

Se han agregado dos nuevos atributos: AsyncTimeoutAttribute y NoAsyncTimeoutAttribute. Estos atributos permiten controlar el período de tiempo de espera asincrónico.

Si un método de acción asincrónico llama a un servicio que expone métodos por medio del modelo BeginMethod/EndMethod, el método de devolución de llamada (es decir, el método que se pasa como parámetro de devolución de llamada asincrónico al método Begin) podría ejecutarse en un subproceso que no se encuentre bajo el control de ASP.NET. En ese caso, HttpContext.Current será null y la aplicación podría experimentar condiciones de carrera al tener acceso a los miembros de la clase AsyncManager, como Parameters. Para asegurarse de tener acceso a la instancia de HttpContext.Current y evitar la condición de carrera, puede restaurar HttpContext.Current llamando a Sync() desde el método de devolución de llamada.

Si la devolución de llamada se completa de manera sincrónica, se ejecutará en un subproceso que se encuentre bajo el control de ASP.NET y las operaciones se serializarán para que no se produzcan problemas de simultaneidad. Llamar a Sync() desde un subproceso que ya se encuentra bajo el control de ASP.NET presenta un comportamiento indefinido.

Siempre se llama al método AcciónCompleted en un subproceso que está bajo el control de ASP.NET. Por consiguiente, no llame a Sync() desde ese método.

Se puede llamar a la devolución de llamada que se pasa al método Begin mediante un subproceso que esté bajo el control de ASP.NET. Así pues, debe comprobar esta condición antes de llamar a Sync(). Si la operación se completa de manera sincrónica (es decir, si CompletedSynchronously es true), la devolución de llamada se ejecuta en el subproceso original y no es preciso llamar a Sync(). Si la operación se completa de forma asincrónica (es decir, si CompletedSynchronously es false), la devolución de llamada se ejecuta en un grupo de subprocesos o en el subproceso del puerto de finalización de E/S, en cuyo caso sí es preciso llamar a Sync().

Para obtener más información sobre el modelo BeginMethod/EndMethod, vea Asynchronous Programming Overview y la entrada Using the BeginMethod/EndMethod pattern with MVC del blog de Rick Anderson.

En la siguiente tabla se muestra una lista de las clases clave para los métodos de acción asincrónicos.

class

Descripción

AsyncController

Proporciona la clase base para los controladores asincrónicos.

AsyncManager

Proporciona las operaciones asincrónicas para la clase AsyncController.

Adiciones de comunidad

AGREGAR
Mostrar:
© 2014 Microsoft