16 điểm bởi GN⁺ 2024-10-24 | 1 bình luận | Chia sẻ qua WhatsApp
  • Lichess là một nền tảng cờ vua mã nguồn mở miễn phí với hàng triệu người chơi trên toàn thế giới
  • Sử dụng tab Network của Chrome DevTools để theo dõi giao tiếp giữa client và server

Kết nối WebSocket

  • Hành vi mạng đáng chú ý đầu tiên là một kết nối WebSocket tới URL tương tự như sau:
wss://socket2.lichess.org/play/H5uHz0egyvIA/v6?sri=bt6QzcyOiZg5&v=0  
  • Giao thức wss biểu thị một kết nối WebSocket được mã hóa dùng TLS
  • WebSocket cho phép giao tiếp song công toàn phần, giúp cập nhật thời gian thực giữa client và server mà không cần các yêu cầu HTTP lặp đi lặp lại

Lượt của người chơi cục bộ

  • Khi thực hiện một hành động, các gói dữ liệu sẽ được trao đổi:
// gửi lúc 22:51:35.280  
{  
  "t": "move",   
  "d": {  
    "u": "d2d4",  
    "l": 32,  
    "a": 1  
  }  
}  
  • Tin nhắn nhận được từ server:
// nhận lúc 22:51:35.312  
{  
  "t": "ack",  
  "d": 1  
}  
  • Điều này cho biết server đã nhận được hành động của chúng ta
// nhận lúc 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  
    }  
  }  
}  
  • Tin nhắn này cung cấp thông tin chi tiết về nước đi chúng ta vừa thực hiện và trạng thái ván cờ đã được cập nhật

Lượt của đối thủ

  • Khi đối thủ thực hiện hành động, ta nhận được một gói tương tự từ server:
// nhận lúc 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"  
      // các nước đi bổ sung có thể có  
    },  
    "clock": {   
      "white": 300,  
      "black": 300  
    }  
  }  
}   
  • Tham số dests liệt kê tất cả các nước đi khả dụng ở vị trí hiện tại

Kiến trúc của Lichess

  • Hệ thống chơi thời gian thực của Lichess chủ yếu gồm hai dịch vụ chính (cả hai đều được viết bằng Scala):
    1. lila: dịch vụ lõi quản lý logic ván cờ, trạng thái, tương tác người dùng và các chức năng cốt lõi khác
    2. lila-ws: dịch vụ chuyên xử lý WebSocket, đóng vai trò cầu nối giữa client và lila

Tổng quan kiến trúc

lila <-> redis <-> lila-ws <-> websocket <-> client  
  • lila giao tiếp với lila-ws thông qua Redis, và lila-ws quản lý các kết nối WebSocket với client

Giao tiếp bằng Redis Pub/Sub

  • Các sự kiện nước đi được publish lên các kênh Redis Pub/Sub, nơi lila đăng ký để xử lý các nước đi
  • Redis Pub/Sub cung cấp cơ chế chuyển phát at-most-once. Tin nhắn có thể bị mất nhưng đổi lại giảm mức sử dụng bộ nhớ

Tính bền vững dữ liệu cuối cùng với MongoDB

  • lila lưu trạng thái ván cờ vào MongoDB, nhưng không lưu ngay lập tức từng nước đi đơn lẻ
  • Thay vào đó, nó đệm các nước đi và lưu theo chu kỳ để giảm tải cho DB
  • Khi có sự kiện quan trọng xảy ra, trạng thái ván cờ sẽ được flush

Tham gia một ván đang diễn ra

  • Khi người chơi kết nối, họ cung cấp tham số v để cho hệ thống biết phiên bản mới nhất của ván cờ mà họ đang nắm được
  • lila-ws sử dụng ConcurrentHashMap để theo dõi và quản lý mọi sự kiện của các ván cờ đang diễn ra

Kết luận

Quy trình thực hiện một nước đi trên Lichess có thể được tóm tắt như sau:

  1. Client thiết lập kết nối WebSocket với lila-ws
  2. Khi người chơi thực hiện một nước đi, client gửi sự kiện nước đi tới lila-ws
  3. lila-ws phản hồi ack để xác nhận đã nhận được nước đi
  4. Sự kiện nước đi được publish lên kênh Redis Pub/Sub và được lila xử lý
  5. lila nhận nước đi, cập nhật trạng thái ván cờ và cuối cùng lưu vào MongoDB. Trạng thái ván cờ đã cập nhật sau đó được gửi trở lại client thông qua lila-ws
  6. Client nhận trạng thái ván cờ đã cập nhật, phản ánh nước đi mới và các thay đổi trong ván cờ

Ý kiến của GN⁺

  • Bài viết này xem xét chi tiết kiến trúc backend và quy trình giúp lichess.org, một nền tảng cờ vua mã nguồn mở phổ biến, vận hành gameplay thời gian thực
  • Nó giới thiệu những yếu tố kỹ thuật quan trọng cần cân nhắc khi xây dựng ứng dụng web thời gian thực, chẳng hạn giao tiếp thời gian thực bằng WebSocket, truyền tin có thể mở rộng bằng Redis Pub/Sub và lưu trữ dữ liệu cuối cùng bằng MongoDB
  • Kiến trúc của Lichess rất phù hợp cho game nhiều người chơi thời gian thực, nhưng các mẫu và công nghệ tương tự cũng có thể áp dụng cho những loại ứng dụng web thời gian thực khác như chat, công cụ cộng tác và feed mạng xã hội
  • Các tính năng thời gian thực có thể cải thiện trải nghiệm và tương tác người dùng, nhưng cũng đặt ra những thách thức kỹ thuật riêng về khả năng mở rộng, độ ổn định và tính nhất quán dữ liệu. Bài viết này đưa ra các chiến lược để giải quyết những thách thức đó
  • Các dự án mã nguồn mở dùng stack công nghệ tương tự bao gồm Socket.IO (framework ứng dụng thời gian thực dựa trên Node.js) và RethinkDB (cơ sở dữ liệu NoSQL tối ưu cho ứng dụng web thời gian thực)
  • Phân tích trong bài viết này không dựa trên việc xem xét trực tiếp mã nguồn của Lichess, nên có thể có khác biệt so với cách triển khai thực tế. Tuy nhiên, các khái niệm cốt lõi và mẫu kiến trúc được mô tả vẫn có giá trị
  • Khi thiết kế hệ thống thời gian thực, cần cân nhắc cẩn thận liệu cơ chế chuyển phát at-most-once (có thể mất tin nhắn) hay at-least-once (có thể trùng lặp tin nhắn) sẽ phù hợp hơn. Điều này phụ thuộc vào yêu cầu và các đánh đổi của ứng dụng

1 bình luận

 
GN⁺ 2024-10-24
Ý kiến trên Hacker News
  • Có phàn nàn về cấu trúc tính thời gian của Chess.com. Có vẻ như máy chủ theo dõi thời gian nên bỏ qua thời gian truyền và độ trễ. Điều này đặc biệt bất tiện khi chơi cờ giới hạn thời gian trên ứng dụng di động

    • Cũng có thể là vấn đề ở mã mạng, và lỗi xảy ra khá thường xuyên trong phần puzzle
    • Công nghệ của Chess.com tạo cảm giác còn khá thô
  • Lichess đã chọn cách tiếp cận kiểu StackOverflow và sử dụng máy chủ mạnh

    • Trạng thái ván đấu được lưu định kỳ, nhưng không rõ được lưu ở đâu
    • Chi phí mỗi ván rất thấp: $0.00027, tương đương 1 đô la cho 3,671 ván
    • Đã từng có sự cố ngừng hoạt động kéo dài 10 giờ do phụ thuộc vào một trung tâm dữ liệu duy nhất
  • Việc tính toán nước đi ở phía máy chủ giúp đảm bảo tính nhất quán và tối ưu hiệu năng cho các client có năng lực xử lý hoặc năng lượng hạn chế

    • Đây cũng có thể là cách để hạ thấp rào cản triển khai client phần mềm nguồn mở trên các nền tảng mới
    • Việc triển khai luật cờ vua có thể khá phiền phức, và Lichess cũng từng có lỗi logic
  • Phần giải thích về cách xử lý mất thông điệp trong kênh pub/sub của Redis còn thiếu

  • Tham số l có thể biểu thị độ trễ mà máy chủ quan sát được

  • Việc máy chủ liệt kê và gửi toàn bộ các nước đi hợp lệ tiếp theo là điều gây ngạc nhiên

    • Điều này có thể có lợi cho các client hạn chế, nhưng không rõ có rẻ hơn so với việc tính toán ở phía client hay không
  • Có câu hỏi về cách bảo vệ máy chủ WebSocket

    • Dùng gói miễn phí của Cloudflare sẽ phát sinh độ trễ
    • Có sự tò mò về các giải pháp miễn phí
  • Có người thắc mắc vì sao giao thức lại cần ack

    • WebSocket bọc trong TLS có thể đảm bảo tính toàn vẹn của thông điệp
  • FEN chỉ mã hóa trạng thái bàn cờ, không bao gồm trạng thái ván đấu

    • Dự án scalachess viết bằng Scala vẫn đang được duy trì thành công