16 puntos por GN⁺ 2024-10-24 | 1 comentarios | Compartir por WhatsApp
  • Lichess es una plataforma de ajedrez gratuita y de código abierto con millones de jugadores en todo el mundo
  • Usa la pestaña Network de Chrome DevTools para monitorear la comunicación entre el cliente y el servidor

Conexión WebSocket

  • El primer comportamiento de red notable es una conexión WebSocket a una URL similar a esta:
wss://socket2.lichess.org/play/H5uHz0egyvIA/v6?sri=bt6QzcyOiZg5&v=0  
  • El protocolo wss indica una conexión WebSocket cifrada que usa TLS
  • WebSocket permite comunicación full-duplex, haciendo posibles actualizaciones en tiempo real entre cliente y servidor sin solicitudes HTTP repetitivas

Turno del jugador local

  • Cuando realizas una acción, se intercambian paquetes de datos:
// Enviado a las 22:51:35.280  
{  
  "t": "move",   
  "d": {  
    "u": "d2d4",  
    "l": 32,  
    "a": 1  
  }  
}  
  • Mensaje recibido del servidor:
// Recibido a las 22:51:35.312  
{  
  "t": "ack",  
  "d": 1  
}  
  • Esto indica que el servidor recibió nuestra acción
// Recibido a las 22:51:35.312  
{  
  "t": "move",  
  "v": 1,  
  "d": {  
    "uci": "d2d4",  
    "san": "d4",  
    "fen": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR",  
    "ply": 1,  
    "clock": {  
      "white": 300,  
      "black": 300  
    }  
  }  
}  
  • Este mensaje proporciona información detallada sobre la acción que hicimos y el estado actualizado de la partida

Turno del oponente

  • Cuando el oponente realiza una acción, recibimos un paquete similar desde el servidor:
// Recibido a las 22:51:43.489  
{   
  "t": "move",  
  "v": 2,  
  "d": {  
    "uci": "d7d5",  
    "san": "d5",  
    "fen": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR",  
    "ply": 2,  
    "dests": {  
      "c2": "c3c4",  
      "g2": "g3g4"  
      // Más movimientos posibles  
    },  
    "clock": {   
      "white": 300,  
      "black": 300  
    }  
  }  
}   
  • El parámetro dests enumera todos los movimientos disponibles desde la posición actual

Arquitectura de Lichess

  • El sistema de juego en tiempo real de Lichess está compuesto principalmente por dos servicios clave (ambos escritos en Scala):
    1. lila: el servicio central que gestiona la lógica del juego, el estado, las interacciones de usuario y otras funciones principales
    2. lila-ws: un servicio especializado en manejar WebSocket que actúa como puente entre el cliente y lila

Vista general de la arquitectura

lila <-> redis <-> lila-ws <-> websocket <-> client  
  • lila se comunica con lila-ws a través de Redis, y este administra la conexión WebSocket con el cliente

Comunicación con Redis Pub/Sub

  • Los eventos de movimiento se publican en canales Redis Pub/Sub, donde lila está suscrito para procesarlos
  • Redis Pub/Sub ofrece entrega at-most-once. Puede haber pérdida de mensajes, pero se reduce el uso de memoria

Persistencia final de datos en MongoDB

  • lila guarda el estado de la partida en MongoDB, pero no persiste inmediatamente cada movimiento individual
  • En su lugar, bufferiza los movimientos y los guarda periódicamente para reducir la carga en la base de datos
  • Cuando ocurre un evento importante, el estado de la partida se vacía al almacenamiento

Unirse a una partida en curso

  • Cuando un jugador se conecta, proporciona el parámetro v para informar al sistema cuál es la versión más reciente de la partida que conoce
  • lila-ws usa ConcurrentHashMap para rastrear y administrar todos los eventos de las partidas en curso

Cierre

El proceso de una jugada en Lichess se puede resumir así:

  1. El cliente establece una conexión WebSocket con lila-ws
  2. Cuando el jugador hace una jugada, el cliente envía un evento de movimiento a lila-ws
  3. lila-ws responde con un ack confirmando la recepción del movimiento
  4. El evento de movimiento se publica en un canal Redis Pub/Sub y lila lo procesa
  5. lila recibe el movimiento, actualiza el estado de la partida y finalmente lo guarda en MongoDB. El estado actualizado de la partida se vuelve a enviar al cliente a través de lila-ws
  6. El cliente recibe el estado actualizado de la partida, reflejando la nueva jugada y los cambios en el estado del juego

Opinión de GN⁺

  • Esta publicación analiza en detalle la arquitectura backend y el proceso que hacen posible el gameplay en tiempo real de lichess.org, una popular plataforma de ajedrez de código abierto
  • Presenta elementos técnicos clave a considerar al construir aplicaciones web en tiempo real, como la comunicación en tiempo real con WebSocket, la mensajería escalable mediante Redis Pub/Sub y el almacenamiento final de datos en MongoDB
  • La arquitectura de Lichess es muy adecuada para juegos multijugador en tiempo real, pero patrones y tecnologías similares también pueden aplicarse a otros tipos de webapps en tiempo real, como chat, herramientas de colaboración y feeds de redes sociales
  • Las funciones en tiempo real pueden mejorar la experiencia de usuario y la interacción, pero también plantean desafíos técnicos propios como escalabilidad, confiabilidad y consistencia de datos. Esta publicación ofrece estrategias para abordar esos retos
  • Entre los proyectos de código abierto con un stack tecnológico similar están Socket.IO (framework para aplicaciones en tiempo real basado en Node.js) y RethinkDB (base de datos NoSQL optimizada para webapps en tiempo real)
  • Este análisis no revisó directamente el código fuente de Lichess, por lo que la implementación real puede diferir. Aun así, los conceptos básicos y patrones de arquitectura descritos siguen siendo válidos
  • Al diseñar sistemas en tiempo real, es importante considerar cuidadosamente si conviene más una entrega at-most-once (posibilidad de pérdida de mensajes) o at-least-once (posibilidad de mensajes duplicados). Eso depende de los requisitos y trade-offs de la aplicación

1 comentarios

 
GN⁺ 2024-10-24
Opiniones de Hacker News
  • Hay quejas sobre la estructura del tiempo en Chess.com. Parece que el servidor lleva el tiempo, así que ignora el tiempo de transmisión y la latencia. Esto resulta especialmente incómodo al jugar partidas con límite de tiempo desde un cliente móvil

    • También podría ser un problema del código de red, y en los rompecabezas ocurren errores con frecuencia
    • La tecnología de Chess.com se siente tosca
  • Lichess eligió un enfoque al estilo StackOverflow y usa servidores potentes

    • Guarda periódicamente el estado de la partida, pero no está claro dónde lo almacena
    • El costo por partida es muy bajo: $0.00027, 1 dólar por 3,671 partidas
    • La dependencia de un solo centro de datos llegó a provocar una caída de 10 horas
  • Calcular los movimientos del lado del servidor garantiza consistencia y optimiza el rendimiento para clientes con capacidad de procesamiento o energía limitadas

    • Podría ser para reducir la barrera de implementación de clientes de software de código abierto en nuevas plataformas
    • Implementar las reglas del ajedrez puede ser engorroso, y Lichess también tuvo en su momento errores de lógica
  • Falta explicación sobre cómo manejan la pérdida de mensajes en los canales pub/sub de Redis

  • El parámetro "l" podría indicar la latencia observada por el servidor

  • Sorprende que el servidor enumere y envíe todos los movimientos legales siguientes

    • Puede beneficiar a clientes limitados, pero queda la duda de si es más barato que calcularlo del lado del cliente
  • Hay preguntas sobre cómo proteger el servidor de WebSocket

    • Usar el plan gratuito de Cloudflare introduce latencia
    • Hay curiosidad por una solución gratuita
  • Hay quien se pregunta por qué el protocolo necesita ack

    • Un WebSocket envuelto en TLS podría garantizar la integridad de los mensajes
  • FEN solo codifica el estado del tablero; no incluye el estado de la partida

    • El proyecto scalachess, escrito en Scala, se ha mantenido con éxito