Diseño del servidor multijugador de Ideateca (II Parte)

Continuando con el post anterior en el que hablábamos del servidor de juegos de Ideateca, en éste voy a comentar el diseño de la librería de comunicaciones y qué es un diseño SEDA y por qué es tan bueno para estos sistemas.

El diseño de la librería de comunicaciones que da soporte al servidor de Ideateca tiene como principal objetivo escalar muy bien para cargas muy altas de usuarios.

La primera decisión de diseño es evidente, I/O asíncrona. La implementación utiliza tres descriptores de I/O para gestionar peticiones de conexión, peticiones de lectura y peticiones escritura.

Una vez tomada esa decisión queda cómo arquitecturar el resto de elementos. Para ello, basta observar el ciclo de vida de un mensaje. Éste debe ser recibido, decodificado (traducido desde el formato de serialización) y procesado (es decir, realizar la invocación sobre el objeto remoto registrado al que va dirigido el mensaje).

Teniendo lo anterior en cuenta, y sabiendo que el mayor problema a la hora de escalar son el número de hilos y que es muy importante la productividad (frente a la velocidad de procesamiento; mayor velocidad de procesamiento por petición no implica mayor productividad y viceversa), el diseño de la librería está basado en un enfoque SEDA (Stage Event Driven Architecture).

Dicho diseño propone partir el diseño en las fases de tratamiento de mensaje, frente a la tentadora posibilidad (y muy común) de diseñar centrándose en el ciclo de vida de la petición. Esta idea no es del todo innovadora, ya que supone la aplicación directa del conocido diseño en “pipeline” de microprocesadores bien conocido por ingenieros en electrónica.

Diseño centrado en petición

diseño centrado en petición

Diseño centrado en fases

diseño centrado en fases

Como se puede observar, la productividad aumenta considerablemente.

Para comprender completamente el diseño de la librería de comunicaciones, es necesario profundizar en el concepto de diseño orientado a etapas.

Se ha mostrado a vista de pájaro el principio SEDA, consistente en dividir el trabajo de procesamiento de una petición en etapas desacopladas mediante colas, y cómo esta organización favorece la productividad del sistema. Es importante entender que de forma subyacente, un sistema SEDA es un sistema de colas. Por tanto se beneficia enormemente de las capacidades de análisis matemático que surgen en dichos sistemas, pudiendo estudiar analíticamente cuando añadir servidores a o colas a una determinada etapa para mejorar el proceso, cuando unir etapas, cuando separarlas, estudiar los cuellos de botella de forma y replicar los recursos involucrados, etc.

Los patrones de diseño relacionados con este tipo de sistemas son los siguientes:

  • Patrón “Wrap”: Se trata de acondicionar una tarea al sistema de procesamiento SEDA, utilizando una cola como elemento de entrada a la etapa.

patron wrap

  • Patrón “Pipeline” y “Partition”: Dividen una tarea en una serie de etapas que permitan mejorar el rendimiento del sistema gracias a una mejora en la localidad o en la arquitectura del sistema colas.

patrón pipeline

  • Patrón “Combine”: Es el patrón opuesto a los anteriores, que permite unir varias etapas en una sola, con la ventaja de reducir la complejidad total del sistema si es que las etapas permiten dicha unión.

patrón combine

  • Patrón “Replicate”: Instancia una etapa más de una vez, generalmente asociada si es posible a un conjunto diferente de recursos bien físicos, bien lógicos. Permite implementar directamente el concepto de redundancia para asegurar la fiabilidad del servicio.

patrón replicate

Para dar más detalles del diseño e implementación de la librería, básicamente hay que tener primero en cuenta otras peculiaridades de los sistemas que van a desarrollarse sobre ella. Generalmente las discusiones sobre servidores o proveedores de servicios se centran en aquellos en que las peticiones son independientes entre sí, no alteran un estado central, es decir, no son transaccionales. Un ejemplo es un servidor de tráfico HTTP. Otro puede ser un servidor de juegos de mundos persistentes (aunque estos necesiten un control microtransaccional) o juegos arcade (también con control microtransaccional). Sin embargo en un juego basado en turnos es muy importante el orden de los mensajes y que estos modifiquen el modelo de forma atómica.

Llegados a este punto se abren dos discusiones; una para juegos arcade donde el modelo es inherentemente multihilo, no es problemático que los mensajes lleguen en desorden parcial y se necesita un protocolo de transporte no orientado a conexión para maximizar el ancho de banda de mensajes. Por otro lado,  están los juegos por turnos, inherentemente monohilo o transaccionales, donde es muy importante asegurar el orden de los mensajes y el protocolo de transporte debe asegurar la entrega de los mensajes de los mismos.

Para cada caso, la librería simplemente elige unos canales de proceso internos diferentes. En el caso arcade, transporte UDP, y el sistema de entrega de mensajes a objetos es simplemente un pool indiferenciado de hilos, escalable hasta el máximo configurado en función de la máquina física. En el caso por turnos, transporte TCP y un pool especializado de hilos donde cada objeto es servido siempre por el mismo hilo para asegurar la atomicidad de la ejecución de mensajes sin sacrificar rendimiento o exponiendo el sistema a una excesiva contención de hilos.

Dada la naturaleza asíncrona del servidor y la necesaria optimización de recursos, se contempla el envío de mensajes retrasados, que la librería (generalmente en el lado cliente) gestionará cuando venzan los contadores del mensaje para permitir al servidor progresar y optimizar sus recursos, de forma que los clientes puedan mostrar adecuadamente datos en las vistas. Asimismo se contemplan mensajes de sincronismo para permitir evolucionar la aplicación de manera igual en todos los clientes.

Como último apunte, la librería permite el registro de observadores de paquetes y estados de conexión, e incluso que estos observadores inyecten paquetes de forma autónoma o los modifiquen e incluso denieguen su posterior procesamiento.

Imágenes extraídas de : An Architecture for Highly Concurrent,Well-Conditioned Internet Services