Mejorando la experiencia en ASP.NET MVC (I)

Este es el primer articulo de una serie de 3 articulos, que pretenden mostrar algunos de mis experimentos y resultados con ASP.NET, tocando fundamentalmente la experiencia de respuesta al usuario. Como Uds saben no hay nada peor que un sitio/pagina que tarde demasiado en realizar una tarea y mientras mas rapido se complete la tarea mucho mejor y si la tarea involucra procesos que no podemos controlar pues hay algunas tecnicas que si podemos aplicar, una de ellas es motivo de este primer post de la serie.

Utilizando controladores asincronos (AsynController)

Vamos a pensar en una aplicacion de ejemplo irrisoriamente simple, lo unico que hara es recuperar de internet una lista de todos los post de un blog mediante RSS, para ello normalmente utilizariamos codigo como el siguiente:

1 public class RssFeed 2 { 3 public event EventHandler<RssEventArgs> GetRssFeedAsyncCompleted; 4 5 // Synchronous model methods 6 public IEnumerable<SyndicationItem> GetRssFeed(string uri) 7 { 8 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 9 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 10 11 using (XmlReader reader = XmlReader.Create(response.GetResponseStream())) 12 { 13 SyndicationFeed feed = SyndicationFeed.Load(reader); 14 return feed.Items; 15 } 16 } 17 18 // Asynchronous model methods 19 public void GetRssFeedAsync(string uri) 20 { 21 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 22 request.BeginGetResponse(new AsyncCallback(OnGetRssFeedAsyncCompleted), request); 23 } 24 25 private void OnGetRssFeedAsyncCompleted(IAsyncResult result) 26 { 27 HttpWebRequest request = (HttpWebRequest)result.AsyncState; 28 HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result); 29 30 using (XmlReader reader = XmlReader.Create(response.GetResponseStream())) 31 { 32 SyndicationFeed feed = SyndicationFeed.Load(reader); 33 if (GetRssFeedAsyncCompleted != null) 34 GetRssFeedAsyncCompleted(this, new RssEventArgs { Items = feed.Items }); 35 } 36 } 37 }

 

1 public class NormalController : Controller 2 { 3 public ActionResult Index() 4 { 5 Stopwatch clock = new Stopwatch(); 6 clock.Start(); 7 RssFeed feed = new RssFeed(); 8 IEnumerable<SyndicationItem> items = 9 feed.GetRssFeed("http://geeks.ms/blogs/MainFeed.aspx"); 10 clock.Stop(); 11 TimeMeasure result = new TimeMeasure(); 12 result.TimeElapsed = clock.ElapsedMilliseconds; 13 result.Data = items; 14 return View(result); 15 } 16 17 }

Por favor omitan el codigo de medicion del tiempo relacionado a la clase Stopwatch, en el codigo anterior la linea 9 es la que realiza la accion del cargado del Rss Feed. El resultado de ejecutar este controlador es un tiempo de carga aproximado de 14 segundos:

image

Ahora utilizando un Controlador Asincrono, se necesita el siguiente codigo:

1 public class ParallelController : AsyncController 2 { 3 public void IndexAsync() 4 { 5 Stopwatch clock = new Stopwatch(); 6 clock.Start(); 7 8 AsyncManager.OutstandingOperations.Increment(); 9 10 RssFeed feed = new RssFeed(); 11 feed.GetRssFeedAsyncCompleted += (s, e) => 12 { 13 AsyncManager.Parameters["items"] = e.Items; 14 AsyncManager.Parameters["clock"] = clock; 15 AsyncManager.OutstandingOperations.Decrement(); 16 }; 17 feed.GetRssFeedAsync("http://geeks.ms/blogs/MainFeed.aspx"); 18 } 19 20 public ActionResult IndexCompleted(IEnumerable<SyndicationItem> items, Stopwatch clock) 21 { 22 //ViewData["SyndicationItems"] = items; 23 clock.Stop(); 24 TimeMeasure result = new TimeMeasure(); 25 result.TimeElapsed = clock.ElapsedMilliseconds; 26 result.Data = items; 27 return View(result); 28 } 29 30 }

Como pueden observar en el codigo anterior hay algunos elementos que debemos destacar:

  • El Controlador ahora hereda de AsyncController
  • Existen dos metodos necesarios <Name>Async y su contraparte <Name>Completed, en nuestro caso son IndexAsync e IndexCompleted respectivamente.
  • El paso de parametros se lo realiza mediante una clase especifica llamada AsyncManager.
  • La invocacion simultanea de varios usuarios es controlada, mediante el incremento/decremento de un contador manipulado tambien por AsyncManager.

Finalmente el resultado obtenido es la reduccion del tiempo en 4 segundos, tal como se muestra a continuacion:

image

La reduccion no es significativa a simple vista pero para los que desarrollamos y tenemos que pensar en cuestiones de performance 4 segundos es una diferencia notable Smile Pero los AsyncControllers no han sido pensados para reducir velocidad per-se, probablemente aqui es uno de esos efectos colaterales bien deseados. La utilizacion mas importante de los AsyncControllers es evitar el bloqueo del Servidor Web, en la atencion de multiples peticiones concurrentes, esto traera repito el efecto colateral de que un servidor mas libre puede atender mas usuarios y por ende se siente mucho mas rapido.

Espero que esto les sirva a todos Uds, un abrazo y feliz 2012!!!

3 comentarios en “Mejorando la experiencia en ASP.NET MVC (I)”

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *