- 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
wssindique 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
destsliste 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) :
lila: le service principal qui gère les fonctionnalités cœur comme la logique du jeu, l’état et les interactions utilisateurlila-ws: un service spécialisé dans la gestion des WebSocket qui sert de pont entre le client etlila
Vue d’ensemble de l’architecture
lila <-> redis <-> lila-ws <-> websocket <-> client
lilacommunique aveclila-wsvia 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
lilaest 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
lilastocke 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
vpour indiquer au système la version la plus récente de la partie qu’il connaît lila-wsutiliseConcurrentHashMappour 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 :
- Le client établit une connexion WebSocket avec
lila-ws - Quand le joueur joue un coup, le client envoie un événement de coup à
lila-ws lila-wsrenvoie une réponseackconfirmant la réception du coup- L’événement de coup est publié dans un canal Redis Pub/Sub puis traité par
lila lilareç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 vialila-ws- 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
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.
Lichess a choisi une approche à la StackOverflow et utilise des serveurs puissants.
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.
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.
Il y a une question sur la manière de protéger un serveur WebSocket.
Certains se demandent pourquoi le protocole a besoin d’un ack.
Le FEN n’encode que l’état de l’échiquier et n’inclut pas l’état de la partie.