16 points par GN⁺ 2024-10-24 | 1 commentaires | Partager sur WhatsApp
  • Lichess est une plateforme d’échecs open source gratuite comptant des millions de joueurs dans le monde
  • Utilisation de l’onglet Network de Chrome DevTools pour surveiller les communications entre le client et le serveur

Connexion WebSocket

  • Le premier comportement réseau notable est une connexion WebSocket vers une URL similaire à celle-ci :
wss://socket2.lichess.org/play/H5uHz0egyvIA/v6?sri=bt6QzcyOiZg5&v=0  
  • Le protocole wss indique une connexion WebSocket chiffrée utilisant TLS
  • WebSocket permet une communication bidirectionnelle full-duplex, rendant possibles des mises à jour en temps réel entre le client et le serveur sans requêtes HTTP répétées

Tour du joueur local

  • Quand une action est effectuée, des paquets de données sont échangés :
// envoyé à 22:51:35.280  
{  
  "t": "move",   
  "d": {  
    "u": "d2d4",  
    "l": 32,  
    "a": 1  
  }  
}  
  • Message reçu du serveur :
// reçu à 22:51:35.312  
{  
  "t": "ack",  
  "d": 1  
}  
  • Il indique que le serveur a bien reçu notre action
// reçu à 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  
    }  
  }  
}  
  • Ce message fournit des informations détaillées sur le coup que nous avons joué et l’état mis à jour de la partie

Tour de l’adversaire

  • Quand l’adversaire joue, on reçoit un paquet similaire depuis le serveur :
// reçu à 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"  
      // autres coups possibles  
    },  
    "clock": {   
      "white": 300,  
      "black": 300  
    }  
  }  
}   
  • Le paramètre dests liste tous les coups disponibles depuis la position actuelle

Architecture de Lichess

  • Le système de jeu en temps réel de Lichess se compose principalement de deux services majeurs (tous deux écrits en Scala) :
    1. lila : le service principal qui gère les fonctionnalités cœur comme la logique du jeu, l’état et les interactions utilisateur
    2. lila-ws : un service spécialisé dans la gestion des WebSocket qui sert de pont entre le client et lila

Vue d’ensemble de l’architecture

lila <-> redis <-> lila-ws <-> websocket <-> client  
  • lila communique avec lila-ws via Redis, et celui-ci gère les connexions WebSocket avec les clients

Communication avec Redis Pub/Sub

  • Les événements de coup sont publiés dans un canal Redis Pub/Sub, auquel lila est abonné pour traiter les coups
  • Redis Pub/Sub fournit une livraison at-most-once. Une perte de messages est possible, mais l’utilisation mémoire est réduite

Persistance finale des données avec MongoDB

  • lila stocke l’état de la partie dans MongoDB, mais n’enregistre pas immédiatement chaque coup individuellement
  • À la place, les coups sont mis en buffer et enregistrés périodiquement afin de réduire la charge sur la base de données
  • Lorsque des événements importants surviennent, l’état de la partie est flushé

Rejoindre une partie en cours

  • Lorsqu’un joueur se connecte, il fournit le paramètre v pour indiquer au système la version la plus récente de la partie qu’il connaît
  • lila-ws utilise ConcurrentHashMap pour suivre et gérer tous les événements des parties en cours

Conclusion

Le processus d’un coup sur Lichess peut être résumé ainsi :

  1. Le client établit une connexion WebSocket avec lila-ws
  2. Quand le joueur joue un coup, le client envoie un événement de coup à lila-ws
  3. lila-ws renvoie une réponse ack confirmant la réception du coup
  4. L’événement de coup est publié dans un canal Redis Pub/Sub puis traité par lila
  5. lila reçoit le coup, met à jour l’état de la partie et le stocke finalement dans MongoDB. L’état mis à jour est ensuite renvoyé au client via lila-ws
  6. Le client reçoit l’état mis à jour de la partie, reflétant le nouveau coup et les changements de position

L’avis de GN⁺

  • Ce billet examine en détail l’architecture back-end et les processus qui rendent possible le gameplay en temps réel de lichess.org, une plateforme d’échecs open source populaire
  • Il présente des éléments techniques clés à prendre en compte lors de la construction d’applications web temps réel, par exemple la communication temps réel avec WebSocket, la diffusion de messages scalable via Redis Pub/Sub et le stockage final des données dans MongoDB
  • L’architecture de Lichess est très adaptée aux jeux multijoueurs en temps réel, mais des schémas et technologies similaires peuvent aussi s’appliquer à d’autres types d’applications web temps réel comme le chat, les outils collaboratifs ou les feeds de réseaux sociaux
  • Les fonctionnalités temps réel peuvent améliorer l’expérience utilisateur et l’interaction, mais elles posent aussi des défis techniques spécifiques, notamment en matière de scalabilité, de fiabilité et de cohérence des données. Ce billet propose des stratégies pour y répondre
  • Parmi les projets open source utilisant une stack technique similaire, on peut citer Socket.IO (framework d’applications temps réel basé sur Node.js) et RethinkDB (base de données NoSQL optimisée pour les applications web temps réel)
  • Cette analyse ne repose pas sur une revue directe du code source de Lichess ; l’implémentation réelle peut donc différer. Cependant, les concepts fondamentaux et les schémas d’architecture décrits restent valables
  • Lors de la conception de systèmes temps réel, il faut examiner attentivement si une livraison at-most-once (risque de perte de messages) ou at-least-once (risque de duplication de messages) est la plus appropriée. Cela dépend des exigences de l’application et des compromis acceptés

1 commentaires

 
GN⁺ 2024-10-24
Avis Hacker News
  • Il y a des plaintes concernant la gestion du temps sur Chess.com. Le serveur semble suivre le temps, ce qui paraît ignorer le temps de transmission et la latence. C’est particulièrement gênant lors des parties chronométrées sur client mobile.

    • Cela peut aussi venir du code réseau, et des erreurs surviennent souvent dans les puzzles.
    • La technologie de Chess.com donne une impression de manque de finition.
  • Lichess a choisi une approche à la StackOverflow et utilise des serveurs puissants.

    • L’état des parties est sauvegardé périodiquement, mais on ne sait pas clairement où.
    • Le coût par partie est très faible : 0,00027 $, soit 1 dollar pour 3 671 parties.
    • Une dépendance à un seul datacenter a déjà provoqué une interruption de 10 heures.
  • Calculer les coups côté serveur garantit la cohérence et optimise les performances des clients disposant d’une puissance de calcul ou d’une énergie limitées.

    • Cela peut aussi viser à réduire la barrière d’implémentation du client du logiciel open source sur de nouvelles plateformes.
    • L’implémentation des règles du jeu d’échecs peut être fastidieuse, et Lichess a aussi eu autrefois des erreurs logiques.
  • L’explication manque sur la façon dont les pertes de messages sont gérées dans les canaux Redis pub/sub.

  • Le paramètre "l" pourrait représenter la latence observée par le serveur.

  • Il est surprenant que le serveur énumère et transmette tous les prochains coups légaux.

    • Cela peut avantager les clients limités, mais on peut se demander si c’est moins coûteux qu’un calcul côté client.
  • Il y a une question sur la manière de protéger un serveur WebSocket.

    • Utiliser l’offre gratuite de Cloudflare introduit de la latence.
    • Il y a de la curiosité pour une solution gratuite.
  • Certains se demandent pourquoi le protocole a besoin d’un ack.

    • Un WebSocket encapsulé dans TLS peut garantir l’intégrité des messages.
  • Le FEN n’encode que l’état de l’échiquier et n’inclut pas l’état de la partie.

    • Le projet scalachess, écrit en Scala, continue d’être maintenu avec succès.