[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.

[Article] Asp.Net Core | Múltiples entornos de trabajo

Una de las nuevas características de Asp.Net Core es la capacidad de trabajar con múltiples entornos de trabajo. Podemos utilizar un entorno de desarrollo, prueba y producción. Esta configuración nos permitirá modificar el comportamiento según el entorno en el cual está ejecutando la aplicación.

Objetivo

El objetivo de esta publicación es tratar los posibles usos de esta nueva característica junto a las buenas prácticas en el desarrollo de aplicaciones.

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

Supongamos que tenemos 3 entornos donde debemos ejecutar o cargar diferentes configuraciones: Development, Staging y Production. Asp.Net Core nos permite distinguir estos entornos por medio de variables denominadas “Enviroment Variable”. la variable a la cual nos referimos se llama ASPNETCORE_ENVIROMENT.  Esta es la que nos permitirá configurar nuestro entorno de trabajo. Los valores más comunes son Development, Staging y Production.

Una vez configurada esta variable podemos detectar en tiempo de ejecución cuál es el valor de esta variable y en qué entorno nos encontramos corriendo la aplicación.

Debemos tener en cuenta que en Linux el nombre es case sensitive, por esta razón, los archivos y las configuraciones serán tomadas como case sensitive como buena práctica.

Development

Este entorno es que usaremos mientras estamos desarrollando nuestra aplicación. ¿Cómo hacemos para configurarlo? debemos hacer botón derecho sobre nuestro proyecto, seleccionar configuración, se abrirá una ventana y seleccionamos de la solapa izquierda “Debug” como podemos ver en la siguiente ventana.

Recordemos que cuando modifico esta pantalla la configuración es modificada en el archivo launchSessting.json dentro de la carpeta properties . En este archivo podemos incluir cualquier entorno que deseemos, por ejemplo, podemos agregar varios perfiles de configuración como podemos ver en el código siguiente:


{

"iisSettings": {

"windowsAuthentication": false,

"anonymousAuthentication": true,

"iisExpress": {

"applicationUrl": "http://localhost:40088/",

"sslPort": 0

}

},

"profiles": {

"IIS Express": {

"commandName": "IISExpress",

"launchBrowser": true,

"environmentVariables": {

"ASPNETCORE_ENVIRONMENT": "Development"

}

},

"IIS Express (Staging)": {

"commandName": "IISExpress",

"launchBrowser": true,

"environmentVariables": {

"ASPNETCORE_ENVIRONMENT": "Staging"

}

}

}

}

Si cambiamos alguna de estas configuraciones en el archivo no se tomarán los efectos automáticamente. Deberemos reiniciar los servidores, sobre todo Kestrel necesitará reiniciar, una vez que se haya modificado para poder detectar correctamente el cambio.

Staging

Este es el entorno que utilizaremos para un entorno de test o preproducción. Casi siempre para un testing final antes de hacer un pasaje a producción. Casi siempre este entorno debería ser un espejo de producción para reducir el impacto de implementación.

Production

Este es el entorno al que muchas veces denominados Vivo. Es un entorno donde configuramos características de seguridad, rendimiento y la fiabilidad de la aplicación. algunas de las características que puede tener el entorno de producción es:

  • Activar funciones de cache
  • Ajustes de archivos del lado del cliente como paquetes, reduccion de archivos js y css o configuraciones de CDN (Content Delivery Network)
  • Desactivar funciones de diagnóstico
  • Activar configuraciones de registro y monitoreo.

La lista puede ser más extensa dependiendo de las necesidades de cada aplicación.

Configurando los entornos

En Windows podemos hacer uso de la configuración por medio de una vez que se está ejecutando nuestra aplicación:

línea de comandos

set ASPNETCORE_ENVIRONMENT=”Development”

PowerShell

$Env:ASPNETCORE_ENVIRONMENT = “Development”

Estos comandos solamente serán validados mientras la ventana donde se está ejecutando este abierta. Si la cerramos perderemos esta configuración. En caso de querer que el valor sea global deberemos configurarlo desde Panel de Control > Sistema > Configuración Avanzada de Sistema, en la solapa Opciones Avanzadas deberemos agregar la variable ASPNETCORE_ENVIRONMENT como vemos en la captura siguiente:

En macOS debemos usar desde el bash el siguiente comando

ASPNETCORE_ENVIRONMENT=Development dotnet run

Ha nivel sistema operativo o maquina debemos configurar las variables en los archivos .bashrc o .bash.profile. debemos editar el archivo y agregar los siguiente:

export ASPNETCORE_ENVIRONMENT=Development

En Linux debemos usar el comando export  desde la consola para la sesión abierta o modificar el archivo bash_profile para todo el sistema operativo o máquina .

Cómo verificar en tiempo de ejecución el entorno

Asp.Net provee un servicio basado en la interface IHostingEnviroment que es disponible por medio de Inyección de Dependencia en el contenedor principal. En el archivo startup.cs podemos ver como es inyectada la variable env  la cual nos permitirá acceder al entorno.


public void Configure(IApplicationBuilder app, IHostingEnvironment env)

{

if (env.IsDevelopment())

{

app.UseDeveloperExceptionPage();

app.UseDatabaseErrorPage();

app.UseBrowserLink();

}

else

{

app.UseExceptionHandler("/Home/Error");

}

Para chequear un entorno específico mientras estamos ejecutando la aplicación, IHostingEnvironment provee el método IsEnviroment( “Nombre del entorno”).

En el código podemos ver que se está preguntando si el entorno es Development por medio del método IsDevelopment() cual cual configurara algunas opciones para este entorno. Por ejemplo, vemos que se configura app.UseBrowserLink(); que es característica propia de visual studio que en producción no usaremos.

También tenemos la posibilidad de usar estas configuraciones por medio de Tag Helper dentro de las Vistas de MVC. Por ejemplo, podemos decirle que utilice archivos css o js no comprimidos en Develoment y que en Staging y Production los use comprimidos.


<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>

Conclusión

Asp.Net Core posee un de características las cuales nosotros como desarrolladores podemos sacarle un muy buen provecho como también un gran control sobre los entornos en los cuales trabajamos habitualmente. Esta configuración nos permite cambiar el comportamiento de nuestra aplicación con un simple cambio de valor en la variable de entorno.