[Article] Arquitectura | Que es MicroServicios

Seguramente habrán escuchado sobre micro-servicios, o  de alguna software que sigue el patrón de una arquitectura de micros-servicios.  Micro-Servicios es un tema que actualmente se encuentra en boca de todos y probablemente escuchemos cada vez más hablar de ello.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance una arquitectura de microservicios. Sus beneficios como también que puntos debemos tener en cuenta para poder llevarla adelante.

Audiencia

Este documento está dirigido a personas que conocen pero no saben que realmente es, como también a personas que no conozcan el tema, o bien profesionales que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Cuando hablamos de una arquitectura de microservicios tenemos que pensar en una aplicación construida sobre una serie de pequeños servicios. Estos tienen la característica de ejecutarse de forma autónoma y también pueden comunicarse entre sí. Uno de los mecanismos comunes de comunicación es por medio de peticiones http hacia sus APIs.

Al referirnos a pequeños servicios podemos decir que cada uno corresponde a un área de negocio de la aplicación como por ejemplo: Clientes, Ventas, Stock, etc. Básicamente el objetivo es tener una cantidad mínima de servicio que manejan cosas comunes para todos como acceso a base de datos o bien servicios de terceros.

Cada servicio es independiente y debe poder ser desplegado sin tener ningún impacto sobre los otros. Otra característica muy interesante es que pueden escribirse en cualquier tecnología y cómo estos se comunican por medio de las APIs pueden hablar sin inconvenientes.

No hay especificaciones de qué tamaño deben tener los micro-servicios, tampoco, cómo debemos dividirlo para una aplicación. Jon Evans publicó que la mejor característica de un microservicio es que puede ser re-escrito nuevamente en 2 semanas.

Arquitectura sin MicroServicios

Para poder entender mejor el objetivo de la arquitectura de microservicios vamos a usar el siguiente ejemplo: Pensemos en una aplicación de gestión para una empresa que se dedica a la venta de productos en línea. Los usuarios interesados en los productos podrán ver, comprar los productos y seguir sus pedidos.

Nuestra primera aproximación de nuestra arquitectura seria algo asi sin la utilización de microservicios:

 

 

Nuestros usuarios entraran a la aplicación, una aplicación web, y podrán visualizar los productos, seleccionarlos,  agregarlos a un carrito y finalmente cerrar su compra junto a su forma de pago y datos de envío. Todo nuestro codigo se ejecutara en el servidor el cual puede conectarse a una base de datos u otro servicio de terceros. En nuestro caso podría ser un servicio de pago especial.

Nuestra aplicación pueda estar separado por módulos con diferentes lógicas de aplicación, pero, esta tiene como resultado siempre el mismo compilado ejecutable. Supongamos que debemos hacer un cambio en el módulo de envíos y debemos implementarlo en producción. Ante esta situación no tenemos opción de tener que implementar todos los módulos aunque la gran mayoría no hayan cambiado.

Otra situación es, que pasa si nuestro sitio tiene comienza a tener mucha demanda. Necesitaremos escalar para poder brindar servicio a todos los usuarios nuevos. En esta arquitectura debería crear nuevas instancias de mi aplicación y usar algún tipo de balanceador de carga para que los usuarios vayan dirigiéndose a cada instancia dependiendo de la carga. Pero, si en realidad es solo el modulo de ventas está sobrecargado, igualmente, deberemos crear instancias totales de nuestra aplicación.

No debemos olvidar que en el punto anterior la aplicación es fácil de gestionar porque posee un solo ejecutable para actualizar todas las instancias de nuestra aplicación de una forma manual o automática.

Arquitectura MicroServicios

 

 

En lugar de tener todo en un solo servicio o ejecutable, cada componente puede auto-ejecutarse y los servicios que lo componente pueden comunicarse entre si sin ningún problema.

Los usuarios pueden interactuar con ellos por medio de la aplicación, que tranquilamente también puede ser un micro-servicio, comunicándose con los servicios de clientes por ejemplo.

Al usar este tipo de arquitectura tenemos los siguientes beneficios:

  • Como la lógica de negocio está bien separada o sectorizada es muy simple de comprender.
  • Cada microservicio es autónomo y puede desplegarse sin afectar a otro. Si el módulo de clientes necesita actualizarse no afectará por ejemplo al módulo de proveedores o no será necesario implementarlo nuevamente.
  • Mejor escalamiento debido a que no es necesario replicar toda la aplicación, si no, balancear o replicar los módulos que están teniendo más carga.
  • Podemos tener varios equipos de desarrollo autónomos encargándose cada uno de un módulo o micro-servicio tanto en el desarrollo como en la gestión del mismo.

Si bien este tipo de arquitectura tiene muchos beneficios… no todo es color de rosa. Como toda arquitectura tiene sus pros y sus contras.

Una falsa creencia es que sin microservicios no podemos tener despliegue continuo o entrega continua, esto no es cierto. Grandes empresas como Facebook, Twitter u otras poseen este tipo de capacidad sin la utilización de una arquitectura de microservicios en su totalidad.

Los micro-servicios agregan complejidad en nuestra gestión. Si tenemos mucha cantidad de servicios debemos tomarnos el tiempo de crear una buena estrategia gestión. Deben estar bien monitoreados, debemos saber exactamente cual esta fallando, gestionar sus fallos, velar por la consistencia de datos y tener una buenas estrategias de pruebas.

Martin Fowler dice : “hay muchas formas de gestionar todo esto, pero necesitamos esfuerzo extra, y nadie que conozco en desarrollo software parece tener muchísimo tiempo libre.”

Si nuestra aplicación es muy simple tal vez no sea necesario usar micro-servicios. Pero si nuestra aplicación crece y comienza a volverse compleja vale la pena invertir esfuerzo en llevarla a este tipo de arquitectura. Grandes empresas, clientes nuestros, se han encontrado con que gestionar la aplicación en un solo componente con el tiempo es cada vez mas complejo y han empezado a separar sus aplicaciones en microservicios o bien, los proyectos nuevos, desarrollarlos con una arquitectura de micro-servicios.

Conclusión

La arquitectura de micro-servicios no es simplemente una moda. Para una empresa que hoy no posee esta arquitectura es aconsejable que vayan hacia ella poco a poco, manteniendo las aplicaciones actuales e ir separando las partes críticas en micro-servicios mediante algunas estrategias como Branch By Abstration. Esta estrategia hablaremos en algún post junto a Visual Studio On-Line. En azure podemos usar docker para llevarla adelante. En próximo post estaremos viendo como usar docket en azure.

[Article] Azure | Content Delivery Network (CDN)

Hoy quiero tocar el tema de CDN específicamente Azure Content Delivery Network. Esta herramienta nos ofrece una solución global para tener contenido disponible usando un ancho de banda de gran velocidad. En esta red podemos almacenar objetos en cache publicos tales como archivos, aplicaciones, maquinas virtuales o cualquier objeto que pueda almacenarse en vía http o https.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance del manejo CDN para mejorar la performance y la escalabilidad en nuestras aplicaciones Asp.Net Core.

Audiencia

Este documento está dirigido a desarrolloradores que quieren conocer Asp.Net core y el uso de Azure CDN o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Usualmente CDN lo utilizamos para entregar contenido estático, como imágenes, archivos javascript, archivos de estilos css, etc. Aunque en su gran mayoría se usa para esto, también, podemos utilizar para brindar contenido dinámico almacenando su parametrización y devolviendo el mismo resultado.

Una de las principales ventajas del uso es que posee una latencia muy pequeña lo cual hace que se pueda entregar contenido extremadamente rápido sin estar atado a una ubicación geográfica o bien brindando lo más cerca posible del usuario que la solicita.

 

Usar CDN no ayuda a reducir la carga de la aplicación ya que liberamos a los servidores de procesamiento innecesario al entregar el contenido. Esto aumenta el rendimiento y reduce el costo del consumo de red como también aumentando la disponibilidad.

Veamos algunos casos típicos donde podemos usar CDN:

  • Recursos Estáticos: Nuestros sitios web contienen una gran cantidad de contenidos estáticos como imágenes, hojas de estilo, documentos, archivos, páginas web completas o parciales o cualquier contenido que no necesita ser procesado por el servidor.
  • Contenido Estático para dispositivos móviles: Por medio de servicio web se puede proporcionar datos estáticos a este tipo de clientes.
  • Media y Video: Podemos transferir videos pequeños. El gran beneficio es que baja mucha la latencia y aumenta la disponibilidad de este tipo de contenido. Algo para tener en cuenta es que Azure Media Services puede ser integrado con Azure CDN para entregar contenido.
  • Aumento de Demanda: podemos evitar que el aumentar la demanda no aumente nuestros costos de consumo. Por ejemplo, si nuestra aplicación lanza una nueva versión, aumentará la demanda de nuestros archivos y contenido para las nuevas características.

Ahora bien, qué debemos tener en cuenta para el uso o implementación:

  • Debemos tenerlo en cuenta durante nuestro proceso de desarrollo e implementación de contenido estático y recursos de nuestras aplicaciones.
  • Debemos considerar que nuestro contenido será actualizado, por esto, debe estar versionado.
  • Es difícil realizar pruebas locales o probar aplicaciones locales.
  • El contenido público es enviado desde dominios diferentes lo cual puede afectar a los resultado SEO.
  • Podemos encontrarnos con entornos donde no podamos consumir recursos directamente desde el CDN. Para esto es necesario hacer una implementación especial.
  • Si la red es privada, como una empresa, tal vez no sea una buena opción por restricciones de seguridad de la misma red.}

Usar CDN es una buena práctica para minimizar la carga, maximizar la disponibilidad y rendimiento de nuestras aplicaciones. Es altamente recomendable para implementar en nuestras aplicaciones web. Veamos algunas estrategias:

Entrega

  • CDN solamente necesita un endpoint HTTP o HTTPS la cual será utilizada para acceder y para almacenar el contenido.
  • Debemos recordar que el Blob al cual esté direccionado debe ser público.
  • Si nuestro lo seteamos como root en una WebApp todo el sitio quedará disponible por CDN.
  • Cuando las fuentes son dinámicas, podemos utilizar parametros via QueryString como llaves para almacenar en caché el contenido de este tipo.
  • Debemos recordar que los recursos del tipo blobs son sensibles a mayusculas y minusculas.

Despliegue

  • El contenido puede necesitar ser desplegado inmediatamente. Debemos considerar que será necesario un mecanismo de control de versiones para poder entregar las el contenido correctamente.
  • La comprensión, unión y minificación de archivos son técnicas utilizadas para reducir el tiempo de carga de los clientes y son compatibles con CDN.
  • Al utilizar la compresión de contenido será enviado en un formato zip o gzip. Por default Azure comprimira el contenido si el procesador se encuentra menos de un 50% de consumo.

Enrutamiento y control de versiones

  • Si necesitamos mantener control de versiones podemos crear un nuevo CDN y conservar el antiguo para versiones anteriores de nuestra aplicación.
  • La implementación de nuevas versiones de contenido estático será necesario tener en cuenta si fueron almacenados en el caché del CDN.
  • Podemos restringir acceso por país. Azure CDN nos permite filtrar contenido por origen de la petición y restringir la entrega del contenido.

Control de Caché

  • Debemos considerar el almacenamiento del caché en el sistema. Podemos configurar propiedades de caché desde el CDN y para el cliente podemos configurar los encabezados HTTP.
  • Podemos purgar el caché eliminandolos manualmente, convertir el blob en privado. Debemos tener en cuenta que los archivos solo serán eliminados cuando el tiempo de vida expire realmente si no se especificado un tiempo de cache.
  • En las aplicaciones web podemos establecer el almacenamiento de cache y su caducidad mediante el elemento clientCache del namespace system.webServer / staticContent en el archivo web.config.
  • Si el contenido es dinámico no debemos olvidar setear la propiedad Cache.SetExpires en cada página.

Seguridad

  • En el CDN puede entregar contenido a través de HTTPS utilizando certificados proporcionado por el mismo. También estará disponible en HTTP ya que no es posible bloquear los elementos por este canal.
  • Podemos tener algunos inconvenientes al usar peticiones AJAX debido a que se hace uso del objeto XMLHttpRequest cuando son solicitados datos, imágenes o fuentes de un dominio diferente. La gran mayoría de los navegadores evitan el consumo de objetos cruzados (CORS) si el servidor no está configurado correctamente.

Dominios personalizados

  • Podemos usar dominios personalizados y utilizarlo para acceder a recursos a través de CDN.
  • Podemos utilizar subdominios utilizando registros CNAME en nuestros CDN. Recordemos que si usamos este método no podremos usar SSL ya que el CDN utiliza sus propios certificados.

Tips para SEO

  • Debemos incluir el encabezado REL en cada una de las paginas o los recursos.
  • Utilizar un subdominio CNAME para acceder a los recursos.}
  • Tener en cuenta que la dirección IP del CDN puede ser diferente del pais donde se encuentra su aplicación.
  • Mantener la misma estructura si estamos usando almacenamiento Blob Azure

Supervisión y registro

Debemos agregar en nuestra estrategia de construcción de aplicaciones el planeamiento para implementación de CDN para poder detectar y medir fallas o problemas de latencia. Desde el portal de Azure podemos encontrar un panel de administración.

Demos Considerar:

  • Ancho de banda
  • Datos transferidos
  • Hits
  • Estado del caché
  • Cache Hit ratio
  • Solicitudes desde IPV4 / 6

Costos

Algo para tener muy presente es que se cobra por transferencia de información saliente desde nuestro endpoint CDN. Debemos incluir el costo de almacenamiento de recursos que se alojan activamente y sus transacciones. Debemos balancear los tiempo de expiración de caché, no debe ser muy largos para mantener un caché actualizado como no deben ser muy cortos para aumentar los costos.

Agrupamiento y minificación

Una excelente práctica es la agrupación  y la minificación de recursos. Esta práctica reduce notoriamente los tiempos de descarga debido a su reducción en tamaño.

Tenemos varias formas de manejar este tipo de prácticas. Podemos hacerlo desde .Net MVC como también con algunas herramientas de NodeJS. Mi recomendación es ver WebPack, una herramienta muy popular dedica a este tipo de funciones.

Conclusión

CDN posee un gran soporte para la utilización de caché tanto en entrega de contenido estático o contenido dinámico. Podemos aprovechar esta funcionalidad para aumentar el rendimiento y la escalabilidad de nuestras aplicaciones. En futuros artículos veremos como usar Azure CDN.

[Article] Asp.Net Core | Trabajando con Cache Distribuido

Vamos a continuar con el tema de uso de cache pero esta vez lo veremos Distribuido. El caché distribuido puede ayudar no solamente en la performance las aplicaciones sino también en la escalabilidad de nuestras aplicaciones.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance del manejo de del caché distribuido para mejorar la performance y la escalabilidad en nuestras aplicaciones Asp.Net Core.

Audiencia

Este documento está dirigido a desarrolladores que quieren conocer Asp.Net Core y el uso de cache distribuido o personas que desarrollan tareas de Consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

¿Qué es el caché distribuido? Significa que podemos usar caché y múltiples servidores. En el caso de Microsoft Azure puede ser alrededor del mundo. El uso de cache distribuido tiene muchas ventajas como, por ejemplo:

  • Los datos almacenados en el caché siempre son coherentes entre los servidores. No depende de donde el usuario consulte los datos todos verán la misma información.
  • Si es necesario reiniciar un equipo el cache seguirá vivo como también si los servidores necesitan escalar su capacidad. Se pueden agregar o quitar servidores sin necesidad de afectar al cache.
  • Las bases de datos o repositorios de datos tendrán menos consultas, un muchos casos a veces hasta ninguna.

Como cualquier caché existente implementar caché distribuido puede aumentar significativamente la capacidad de respuesta de nuestras aplicaciones debido que los datos son recuperados más rápido que desde una base de datos. También pueden reducir el costo de consumo de nuestros repositorios de bases de datos ya son de menor costo.

Vamos a ver 2 configuraciones una como configurar Redis y el Caché distribuido de sql server. Para esto dependemos de la interface IDistributedCache.

En IDistributedCache tenemos disponible tanto método sincrónicos como asincrónicos. Esta nos permite agregar, recuperar o eliminar elementos que tengamos en el caché. Veamos los métodos:

  • Get, GetAsync Recupera los datos del caché por medio de una key.
  • Set, SetAsync Agrega un tiem al caché por medio de una key.
  • Refresh, RefreshAsync Actualiza el caché por medio de un key reiniciando el tiempo de expiración.
  • Remove, RemoveAsync Elimina un objeto del caché.

Para poder utilizar la interfaz debemos seguir los siguientes pasos:

  1. Implementar en nuestra aplicación paquete nuget.
  2. Configurar en nuestro archivo startup.cs dentro de ConfigureService para que nos lo agregue en nuestro contenedor.
  3. Por último, agregarlo en los constructores de nuestros controller donde lo vamos a utilizar.

Usando Cache Distribuido Redis

¿Qué es Redis? Redis es un cache distribuido en memoria de origen open-source. Podemos usarlo de forma local o en Azure. Azure provee una implementación llamada Azure Redis Cache.

De la misma forma que antes debemos configurar en nuestro archivo Startup.cs una instancia del RedisDistributedCache para poder tenerla disponibles.


/// <summary>
/// Use Redis Cache in Staging
/// </summary>
/// <param name="services"></param>
public void ConfigureStagingServices(IServiceCollection services)
{

services.AddDistributedRedisCache(options =>
{
options.Configuration = "localhost";
options.InstanceName = "SampleInstance";
});
}

Usando Cache Distribuido con SQL Server

SqlSeverCache nos permite utilizar cache distribuido con soporte en base de datos de Sql Server. Con la herramienta de sql-cache podremos crear una tabla con estructura necesaria para tener este soporte.

C:\SqlCacheSample\>dotnet sql-cache create “Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;” dbo DbSQLCache

Como resultado tendremos esta tabla:

Una vez todo listo debemos agregarlos a nuestros servicios de la siguiente manera:


public void ConfigureProductionServices(IServiceCollection services)
{

services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = @"Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;";
options.SchemaName = "dbo";
options.TableName = "TestCache";
});

}

Conclusión

Asp.Net Core posee un gran soporte para la utilización de cache en distribuido tanto sea Redis, Azure Cache Redis o SqlCache el cual nosotros podemos aprovechar para aumentar el rendimiento y la escalabilidad de nuestras aplicaciones. Para tener en cuenta Redis mi elección siempre ya que posee

[Article] Asp.Net Core | Mejorar el rendimiento de nuestras Apps con Cache

Uno de nuestros clientes nos pidió que analicemos su aplicación para que aumente su rendimiento. Lo primero que pensamos es usar caché. Asp.Net Core, como otras versiones, tiene un muy buen soporte de Caché en memoria. Trataremos ese tema a continuación.

Objetivo

El objetivo de esta publicación es tocar las posibles utilizaciones y alcance del manejo de del caché en memoria como también que es y para qué son útiles.
Audiencia

Este documento está dirigido a desarrolladores que quieren conocer Asp.Net Core o personas que desarrollan tareas de consultoría de desarrollo o que simplemente están interesados en leer e investigar sobre la tecnología alcanzada por esta publicación.

Desarrollo

Usar caché puede aumentar significa mente la performance y la escalabilidad de nuestra aplicación reduciendo el costo en la generación de contenido. Una buena recomendación es la utilización de cache en los datos que cambia con poca frecuencia. Por ejemplo, si tenemos datos recuperados de una base de datos que no es modificada nunca o muy pocas veces, podemos guardar en caché estos datos, por un cierto tiempo, sin la necesidad de consultar todo el tiempo la base de datos o su datasource.

Asp.Net Core, como otra versión, tiene diferentes tipos de cache que podemos utilizar. El más común es un caché basado en memoria el cual es el más utilizado. Este caché es almacenado en la memoria del WebServer.

Para usar cache en memoria debemos hacer uso de IMemoryCache primer debemos utilizar el packete nuget “Microsoft.Extensions.Caching.Memory”, luego, esto debemos inyectarlo como servicio. Veamos el siguiente código el siguiente código:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddMvc();
}

public void Configure(IApplicationBuilder app)
{
app.UseMvcWithDefaultRoute();
}
}

La configuración de nuestro controller deberia ser de la siguiente manera:

public class HomeController : Controller
{
private IMemoryCache _cache;

public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}

Un método muy util es TryGetValue. Este método nos provee la capacidad de verificar si existe un objeto en el caché por medio de una Key. Si existe nos devuelve como salida el existente.


public IActionResult CacheTryGetValueSet()
{
DateTime cacheEntry;

// Look for cache key.
if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now;

// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));

// Save data in cache.
_cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
}

return View("Cache", cacheEntry);
}

Unos métodos más simplistas son GetOrCreate(), creo que el nombre lo dice todo y tambien como la gran mayoria de Asp.Net Core tenemos la posibilidad de utilizarlo de un modo asincrónico por medio de GetOrCrareAsyn().

public IActionResult CacheGetOrCreate()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return DateTime.Now;
});

return View("Cache", cacheEntry);
}

public async Task<IActionResult> CacheGetOrCreateAsync()
{
var cacheEntry = await
_cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return Task.FromResult(DateTime.Now);
});

return View("Cache", cacheEntry);
}

Nuestro cache en memoria puede ser parametrizado por medio de MemoryCacheEntryOptions. podemos configurar:

  • Un tiempo de experiencia. Es el máximo tiempo que va a mantenerse este y permite renovarlo continuamente.
  • Si el cache es consultado se renueva el tiempo de expiración.
  • Se puede establecer para que nunca se remueva o expire.
  • Por medio de PostEvictionDelegate podemos asociar une evento cuando el cache sea removido.

Veamos los ejemplos en código:

public IActionResult CreateCallbackEntry()
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Pin to cache.
.SetPriority(CacheItemPriority.NeverRemove)
// Add eviction callback
.RegisterPostEvictionCallback(callback: EvictionCallback, state: this);

_cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);

return RedirectToAction("GetCallbackEntry");
}

public IActionResult GetCallbackEntry()
{
return View("Callback", new CallbackViewModel
{
CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
Message = _cache.Get<string>(CacheKeys.CallbackMessage)
});
}

public IActionResult RemoveCallbackEntry()
{
_cache.Remove(CacheKeys.CallbackEntry);
return RedirectToAction("GetCallbackEntry");
}

private static void EvictionCallback(object key, object value,
EvictionReason reason, object state)
{
var message = $"Entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

Conclusión

Asp.Net Core posee un excelente soporte la utilización de cache en memoria la cual nosotros podemos aprovechar para aumentar el rendimiento de nuestras aplicaciones. En próximo post hablaremos de la utilización de cache distribuido a nivel global y otras técnicas para aumentar el rendimiento de nuestras aplicaciones web.