Aunque hayamos diseñado nuestra aplicación web con el máximo cuidado y tratando de que esté lo más optimizada posible, si tiene éxito y acaba creciendo mucho en cuanto a necesidades de atender usuarios y peticiones, llegará un momento en el que no de más de si y sea necesario hacerla crecer.

Una aplicación que reciba simplemente unas pocas peticiones por segundo va a funcionar sin problema en cualquier servidor barato, pero si el número de peticiones crece y necesitamos atender a decenas, cientos o miles de peticiones por segundo es el momento de pensar en la escalabilidad y el crecimiento.

La escalabilidad de un sistema informático se define como la capacidad de éste para expandirse según las necesidades que se deriven del uso que se le va a dar. De este modo decimos que una aplicación web es escalable si es capaz atender a un número cada vez mayor de usuarios sin necesidad de cambiar el código de la aplicación.

Fundamentalmente existen dos maneras de escalar:

  • Escalado vertical: implica añadir más recursos físicos al sistema actual para que pueda atender más solicitudes. Así, por ejemplo, se le añaden más procesadores o más memoria a un servidor. El escalado vertical presenta un límite claro y llega un punto en el que la aplicación necesita ser rediseñada para poder escalar más. En inglés a este tipo de escalado se le denomina "Scale Up".
  • Escalado horizontal: en este caso el escalado se consigue simplemente añadiendo más nodos al conjunto del sistema, por ejemplo, poniendo otra máquina en paralelo a funcionar. Este tipo de escalado es más sencillo y no tiene límite, pero implica que las aplicaciones deben estar adaptadas para poder hacerlo, pues implican coordinación, replicación, etc... Se denomina comúnmente como "Scale Out".

El escalado vertical tiene un límite, mientras que el horizontal no.

Evidentemente el escalado que debiéramos buscar siempre es el horizontal. Al menos tener la posibilidad de hacerlo cuando sea necesario. Si nos limitamos a añadir más memoria y procesador al servidor, cuando llegue el momento (y si tenemos suerte llegará) de que la máquina no soporte más, será necesario que pasemos a escalar de manera horizontal. Y si nuestra aplicación no está preparada lo pasaremos muy mal.

Granjas de servidores o Web Farms

Se llama Granja de Servidores o, de manera más común, Web Farm, a un conjunto de servidores que trabajan en paralelo sirviendo la misma aplicación y que son capaces de atender muchas más peticiones que las que sería posible con una sola máquina.

En una Web Farm si se necesita más capacidad basta con añadir un nuevo servidor en paralelo para conseguirla. Se puede añadir incluso un servidor en caliente, sin detener nada y sin que los demás se vean impactados.

En una Web Farm, además, si un servidor se cae o se estropea no pasa absolutamente nada: cualquier otro de los nodos puede atender sus peticiones e incluso podríamos levantar un nuevo servidor en minutos para sustituir al que se ha caído.

Crear una Web Farm es un proceso complejo y se sale por completo del ámbito de un blog como este (en MSDN puedes encontrar instrucciones, pero realmente hay muchas maneras de hacerlo) .

Poner en marcha una de estas granjas implica utilizar algún tipo de balanceador de carga. El balanceador de carga se ocupa de recibir las peticiones de los usuarios y de manera rápida y eficiente dirigirlas a cada una de las máquinas de la granja.

El siguiente esquema muestra conceptualmente cómo funcionaría una de estas granjas:

Server-Farm

Como vemos el balanceador de carga recibe las peticiones de los usuarios, y las reparte entre los diferentes nodos (servidores web) que tienen nuestra aplicación en funcionamiento. Generalmente todos estos compartirán un mismo almacenamiento, tanto para los contenidos como para las bases de datos.

El balanceador puede ser un servidor dedicado con Internet Information Server y el servicio Application Request Routing (ARR) y/o con el IIS Web Farm Framework. También puede haber un servidor con el servicio Network Load Balancing (NLB) instalado o un dispositivo hardware especializado en estos menesteres, como por ejemplo los del conocido fabricante F5.

El modo de redireccionamiento de peticiones desde el balanceador a los nodos puede realizarse de diferentes maneras: round-robin (uno a uno según lleguen), repartiendo de manera diferente, cargando primero uno y luego yendo a los otros, aleatoriamente...

Mantenimiento de sesión

En aplicaciones web la mayor dificultad para conseguir el escalado horizontal suele ser el hecho de que la información de sesión se almacena normalmente en memoria. El motivo es que si no hay afinidad de sesión, quiere decir que cada petición de un mismo usuario se puede recibir en un servidor diferente de cada vez. Por ello, si tenemos información de sesión en memoria que se almacena en una primera petición, al recibir la segunda en una máquina distinta es como si no existiese la sesión anterior, iniciándose una nueva. Esto, aparte de causar problemas a los usuarios, provocará un funcionamiento errático de la aplicación y desde luego no es admisible.

Una opción, recomendable en cualquier caso, es tratar de no utilizar variables de sesión para nada. Pero muchas veces es imposible.

Por regla general lo que se hace en estos casos, cuando queremos que la aplicación funcione en modo granja y al mismo tiempo que use variables de sesión, es cambiar el modo en el que se almacena la información de sesión para que lo haga de manera externa (también llamado Off-Process). Esto implica habitualmente que los datos de las sesiones se almacenen en una base de datos común a todos los servidores, o bien en la memoria de un servidor común usando un protocolo especial.

ASP.NET permite configurar el almacenamiento de sesiones para utilizar cualquier proveedor de almacenamiento de sesión diferente al que usa por defecto (y que guarda todo en la memoria RA M local, no apto para granjas de servidores). Con la plataforma .NET se entregan dos proveedores de almacenamiento de sesión adicionales que permiten almacenar los datos en un servidor ASP.NET central o en una base de datos SQL Server respectivamente.

Para ello basta con establecer el nodo sessionState en el web.config de nuestra aplicación:

sessionState-Mode

Gracias al modelo de extensibilidad mediante proveedores que ofrece ASP.NET es posible escribir nuestros propios proveedores para almacenar la sesión del modo que más nos convenga, si bien no es lo habitual. Esta característica incluso nos permite definir un formato propio para los identificadores de sesión aunque hacerlo no es, en general, una buena idea. Si tienes interés, más info en MSDN.

Cada uno de estos modos tiene su propia configuración particular, aunque en general es relativamente sencillo de poner en marcha.

Modo StateServer

En este modo se utiliza un proceso externo a ASP.NET llamado ASP.NET State Service que se plasma en un ejecutable llamado aspnet_state.exe que podemos encontrar en la carpeta del sistema para la plataforma .NET:

ASPNET_state-exe

Este ejecutable, no obstante, no lo podemos lanzar manualmente, sino que se trata de un servicio que es necesario activar en un servidor común para que actúe de backend para las sesiones de todos los demás servidores de la granja. Para ello iremos al complemento de servicios de ese servidor y lo localizaremos, poniéndolo en marcha:

ASPNET-State-Servicio

Es importante también cambiarlo a modo de activación automática para que no se apague nunca ni siquiera en los reinicios.

Una vez hecho esto lo que tenemos que hacer es configurar cada uno de los nodos en el web.config para que usen ese servidor, lo cual se consigue con la propiedad stateConnectionString:

<configuration>
  <system.web>
    <sessionState mode="StateServer"
      stateConnectionString="tcpip=miServidorDeEstado:42424"
      cookieless="false"
      timeout="30"/>
  </system.web>
</configuration>

Como vemos funciona en el puerto 42424, así que debemos asegurarnos de que el cortafuegos del servidor central de sesiones tiene abierto este puerto TCP.

MUY IMPORTANTE: para que las sesiones funcionen correctamente en todos los servidores éstos deben utilizar las mismas claves de encriptación en el nodo machineKey de su configuración. Para ello debemos generar unas manualmente y asignarlas en ese nodo. En estos artículos de la KB de Microsoft se explica cómo generar esas claves con un programa escrito en C# o en Visual Basic.

También debemos tener en cuenta que todo lo que guardemos en sesión debe ser serializable o no podrá transmitirse y almacenarse en el servidor de estado (eso es algo que deberíamos hacer siempre de todos modos).

Modo SQLServer

En este caso el funcionamiento es parecido, pero en lugar de almacenarse la información en un servidor de estado, se almacena en una base de datos de SQL Server, en un servidor común a todos los nodos.

Para ponerlo en marcha en este caso lo que hay que hacer es crear las tablas pertinentes en una base de datos SQL Server, usando para ello la utilidad aspnet_regsql.exe (ubicada en la misma carpeta que aspnet_state.exe) y en concreto con su parámetro -ssadd.

Luego en el web.config establecemos el modo SQLServer para el estado de sesión y mediante el atributo sqlConnectionString del nodo escribimos la cadena de conexión a la base de datos que albergará el estado de sesión:

<configuration>
  <system.web>
    <sessionState mode="SQLServer"
      sqlConnectionString="Integrated Security=SSPI;data source=SesionesDB;" />
  </system.web>
</configuration>

Una ventaja adicional de tener las sesiones persistidas fuera del proceso (sea en un servidor de estado o en SQL Server) es que en ambos casos la información se almacena de manera persistente mientras dure el tiempo teórico de la sesión. Esto quiere decir que si la sesión dura por ejemplo 20 minutos y en ese tiempo se reinicia IIS por el motivo que sea (una actualización de la app, un reciclado automático, un error...) la sesión seguirá disponible para los usuarios que se reconecten, algo que no ocurre normalmente.

Como contrapartida la gestión de sesiones tiene un rendimiento bastante menor, puesto que hay que serializar, enviar por la red y persistir los datos, y lo contrario cuando se leen.

¡Espero que te resulte útil!

💪🏻 ¿Este post te ha ayudado?, ¿has aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor

Escrito por un humano, no por una IA