- 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
wssindica 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
destsenumera 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):
lila: el servicio central que gestiona la lógica del juego, el estado, las interacciones de usuario y otras funciones principaleslila-ws: un servicio especializado en manejar WebSocket que actúa como puente entre el cliente ylila
Vista general de la arquitectura
lila <-> redis <-> lila-ws <-> websocket <-> client
lilase comunica conlila-wsa 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
lilaestá 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
lilaguarda 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
vpara informar al sistema cuál es la versión más reciente de la partida que conoce lila-wsusaConcurrentHashMappara rastrear y administrar todos los eventos de las partidas en curso
Cierre
El proceso de una jugada en Lichess se puede resumir así:
- El cliente establece una conexión WebSocket con
lila-ws - Cuando el jugador hace una jugada, el cliente envía un evento de movimiento a
lila-ws lila-wsresponde con unackconfirmando la recepción del movimiento- El evento de movimiento se publica en un canal Redis Pub/Sub y
lilalo procesa lilarecibe 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 delila-ws- 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
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
Lichess eligió un enfoque al estilo StackOverflow y usa servidores potentes
Calcular los movimientos del lado del servidor garantiza consistencia y optimiza el rendimiento para clientes con capacidad de procesamiento o energía limitadas
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
Hay preguntas sobre cómo proteger el servidor de WebSocket
Hay quien se pregunta por qué el protocolo necesita
ackFEN solo codifica el estado del tablero; no incluye el estado de la partida