16 ポイント 投稿者 GN⁺ 2024-10-24 | 1件のコメント | WhatsAppで共有
  • Lichess は、世界中で数百万人のプレイヤーを抱える無料のオープンソースチェスプラットフォーム
  • Chrome DevTools の Network タブを使って、クライアントとサーバー間の通信を監視

WebSocket 接続

  • 最初に注目すべきネットワーク動作は、次のような URL への WebSocket 接続:
wss://socket2.lichess.org/play/H5uHz0egyvIA/v6?sri=bt6QzcyOiZg5&v=0  
  • wss プロトコルは、TLS を使う暗号化された WebSocket 接続を表す
  • WebSocket は双方向通信を可能にし、繰り返し HTTP リクエストを行わなくても、クライアントとサーバーの間でリアルタイム更新を実現する

ローカルプレイヤーの手番

  • 操作を行うと、データパケットがやり取りされる:
// 22:51:35.280 に送信  
{  
  "t": "move",   
  "d": {  
    "u": "d2d4",  
    "l": 32,  
    "a": 1  
  }  
}  
  • サーバーから受信したメッセージ:
// 22:51:35.312 に受信  
{  
  "t": "ack",  
  "d": 1  
}  
  • サーバーがこちらの操作を受信したことを知らせる
// 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  
    }  
  }  
}  
  • このメッセージは、こちらが行った手と更新後のゲーム状態に関する詳細情報を提供する

相手の手番

  • 相手が指すと、サーバーから同様のパケットを受信する:
// 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"  
      // 追加で可能な手  
    },  
    "clock": {   
      "white": 300,  
      "black": 300  
    }  
  }  
}   
  • dests パラメータは、現在の局面で可能なすべての手を列挙する

Lichess のアーキテクチャ

  • Lichess のリアルタイム対局システムは、主に 2 つの主要サービス(どちらも Scala で記述)で構成される:
    1. lila: ゲームロジック、状態、ユーザーインタラクションなどの中核機能を管理するコアサービス
    2. lila-ws: クライアントと lila の間のブリッジとして機能する、WebSocket 処理専用サービス

アーキテクチャ概要

lila <-> redis <-> lila-ws <-> websocket <-> client  
  • lila は Redis を通じて lila-ws と通信し、lila-ws がクライアントとの WebSocket 接続を管理する

Redis Pub/Sub を使った通信

  • 手のイベントは Redis Pub/Sub チャンネルに publish され、lila がそれを subscribe して手を処理する
  • Redis Pub/Sub は at-most-once 配信を提供する。メッセージ損失の可能性はあるが、メモリ使用量を抑えられる

MongoDB への最終的なデータ永続化

  • lila はゲーム状態を MongoDB に保存するが、すべての手を即座に保存するわけではない
  • 代わりに、手をバッファリングして定期的に保存することで DB 負荷を下げる
  • 重要なイベントが発生すると、ゲーム状態がフラッシュされる

進行中のゲームに参加する

  • プレイヤーは接続時に v パラメータを渡し、自分が認識しているゲームの最新バージョンをシステムに伝える
  • lila-wsConcurrentHashMap を使って、進行中のゲームのすべてのイベントを追跡・管理する

まとめ

Lichess での手の処理は、要約すると次のようになる:

  1. クライアントが lila-ws に WebSocket 接続を確立
  2. プレイヤーが手を指すと、クライアントが lila-ws に手のイベントを送信
  3. lila-ws は、手を受信したことを確認する ack レスポンスを返す
  4. 手のイベントが Redis Pub/Sub チャンネルに publish され、lila が処理
  5. lila は手を受信してゲーム状態を更新し、最終的に MongoDB に保存する。更新されたゲーム状態は lila-ws を通じて再びクライアントへ送信される
  6. クライアントは、新しい手とゲーム状態の変化を反映した更新済みのゲーム状態を受信する

GN⁺ の見解

  • この投稿は、人気のオープンソースチェスプラットフォーム lichess.org のリアルタイム対局を可能にしているバックエンドアーキテクチャとプロセスを詳しく見ている
  • リアルタイム Web アプリケーションを構築する際に考慮すべき主要な技術要素を紹介している。たとえば、WebSocket を使ったリアルタイム通信、Redis Pub/Sub によるスケーラブルなメッセージ配信、MongoDB への最終的なデータ保存など
  • Lichess のアーキテクチャはリアルタイムのマルチプレイヤーゲームに非常に適しているが、チャット、コラボレーションツール、ソーシャルメディアのフィードなど、他の種類のリアルタイム Web アプリにも同様のパターンと技術を適用できる
  • リアルタイム機能はユーザー体験とインタラクションを向上させる一方で、スケーラビリティ、信頼性、データ整合性といった固有の技術的課題も生じる。この投稿は、それらの課題に対処するための戦略を提示している
  • 類似の技術スタックを使うオープンソースプロジェクトとしては、Socket.IO(Node.js ベースのリアルタイムアプリケーションフレームワーク)や RethinkDB(リアルタイム Web アプリ向けに最適化された NoSQL データベース)などがある
  • この投稿の分析は Lichess のソースコードを直接レビューしたものではないため、実際の実装とは差異がある可能性がある。ただし、説明されている基本概念とアーキテクチャパターンは依然として有効だ
  • リアルタイムシステムを設計する際には、at-most-once(メッセージ損失の可能性あり)と at-least-once(メッセージ重複の可能性あり)のどちらの配信がより適切かを慎重に検討する必要がある。これはアプリケーションの要件とトレードオフによって異なる

1件のコメント

 
GN⁺ 2024-10-24
Hacker Newsのコメント
  • Chess.comの時間管理の仕組みに不満がある。サーバーが時間を追跡しているようで、送信時間や遅延を無視しているように見える。モバイルクライアントで持ち時間ありの対局をするときに特に不便

    • ネットワークコードの問題かもしれず、パズルでもエラーがよく発生する
    • Chess.comの技術は粗い印象がある
  • LichessはStack Overflow的なアプローチを選び、強力なサーバーを使っている

    • ゲーム状態を定期的に保存しているが、どこに保存しているのかは明確ではない
    • 1ゲームあたりのコストは非常に低い: $0.00027、3,671ゲームで1ドル
    • 単一データセンターへの依存により、過去に10時間の停止が発生したことがある
  • サーバー側で指し手を計算することで一貫性を確保し、処理能力や電力が限られたクライアントの性能を最適化できる

    • 新しいプラットフォームでオープンソースソフトウェアのクライアント実装の障壁を下げるためかもしれない
    • チェスのルール実装は面倒になりがちで、Lichessにもかつてロジックエラーがあった
  • Redisのpub/subチャネルでメッセージ損失をどう処理しているのかについて説明が足りない

  • lパラメータは、サーバーで観測された遅延を表している可能性がある

  • サーバーが次の合法手をすべて列挙して送信しているのは驚き

    • 制約の大きいクライアントには有利かもしれないが、クライアント側で計算するより安いのかは疑問
  • WebSocketサーバーをどう保護しているのかという質問がある

    • Cloudflareの無料プランを使うと遅延が発生する
    • 無料のソリューションがあるのか気になっている
  • このプロトコルになぜackが必要なのか疑問

    • TLSで包んだWebSocketならメッセージの完全性を保証できるはず
  • FENは盤面の状態だけをエンコードし、ゲーム状態は含まない

    • Scalaで書かれたscalachessプロジェクトはうまく保守されている