16 คะแนน โดย GN⁺ 2024-10-24 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Lichess เป็นแพลตฟอร์มหมากรุกโอเพนซอร์สฟรีที่มีผู้เล่นหลายล้านคนทั่วโลก
  • ใช้แท็บ Network ของ Chrome DevTools เพื่อติดตามการสื่อสารระหว่างไคลเอนต์กับเซิร์ฟเวอร์

การเชื่อมต่อ WebSocket

  • พฤติกรรมเครือข่ายอย่างแรกที่น่าสังเกตคือการเชื่อมต่อ WebSocket ไปยัง URL ลักษณะคล้ายดังนี้:
wss://socket2.lichess.org/play/H5uHz0egyvIA/v6?sri=bt6QzcyOiZg5&v=0  
  • โปรโตคอล wss หมายถึงการเชื่อมต่อ WebSocket แบบเข้ารหัสที่ใช้ TLS
  • WebSocket รองรับการสื่อสารแบบ full-duplex ทำให้ไคลเอนต์และเซิร์ฟเวอร์อัปเดตข้อมูลแบบเรียลไทม์ได้โดยไม่ต้องส่ง HTTP request ซ้ำๆ

ตาของผู้เล่นฝั่งเรา

  • เมื่อมีการกระทำ จะมีการแลกเปลี่ยนแพ็กเก็ตข้อมูลดังนี้:
// ส่งเมื่อ 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 ประกอบด้วยบริการหลักสองตัวเป็นหลัก (ทั้งคู่เขียนด้วย Scala):
    1. lila: บริการแกนหลักที่จัดการฟังก์ชันสำคัญ เช่น logic ของเกม สถานะ และการโต้ตอบของผู้ใช้
    2. lila-ws: บริการเฉพาะทางด้านการจัดการ WebSocket ที่ทำหน้าที่เป็นสะพานเชื่อมระหว่างไคลเอนต์กับ lila

ภาพรวมสถาปัตยกรรม

lila <-> redis <-> lila-ws <-> websocket <-> client  
  • lila สื่อสารกับ lila-ws ผ่าน Redis และ lila-ws เป็นผู้ดูแลการเชื่อมต่อ WebSocket กับไคลเอนต์

การสื่อสารด้วย Redis Pub/Sub

  • event การเดินหมากจะถูก publish ไปยังช่องทาง Redis Pub/Sub ซึ่ง lila จะ subscribe เพื่อประมวลผลการเดินหมาก
  • Redis Pub/Sub ให้การส่งแบบ at-most-once หมายความว่าข้อความอาจสูญหายได้ แต่ช่วยลดการใช้หน่วยความจำ

การทำให้ข้อมูลคงอยู่ถาวรใน MongoDB ในท้ายที่สุด

  • lila บันทึกสถานะเกมลงใน MongoDB แต่จะไม่บันทึกทุกการเดินหมากแบบทันที
  • แต่จะบัฟเฟอร์การเดินหมากไว้และบันทึกเป็นระยะเพื่อลดภาระของฐานข้อมูล
  • เมื่อเกิด event สำคัญ สถานะเกมจะถูก flush

การเข้าร่วมเกมที่กำลังดำเนินอยู่

  • เมื่อผู้เล่นเชื่อมต่อ จะส่งพารามิเตอร์ v เพื่อบอกระบบว่าตนรู้จักเวอร์ชันล่าสุดของเกมถึงจุดใดแล้ว
  • lila-ws ใช้ ConcurrentHashMap เพื่อติดตามและจัดการ event ทั้งหมดของเกมที่กำลังดำเนินอยู่

สรุปส่งท้าย

กระบวนการเดินหมากบน Lichess สรุปได้ดังนี้:

  1. ไคลเอนต์สร้างการเชื่อมต่อ WebSocket กับ lila-ws
  2. เมื่อผู้เล่นเดินหมาก ไคลเอนต์จะส่ง event การเดินหมากไปยัง lila-ws
  3. lila-ws ตอบกลับด้วย ack เพื่อยืนยันว่าได้รับการเดินหมากแล้ว
  4. event การเดินหมากถูก publish ไปยังช่องทาง Redis Pub/Sub และ lila เป็นผู้ประมวลผล
  5. lila รับการเดินหมาก อัปเดตสถานะเกม และบันทึกลง MongoDB ในท้ายที่สุด จากนั้นสถานะเกมที่อัปเดตแล้วจะถูกส่งกลับไปยังไคลเอนต์ผ่าน lila-ws
  6. ไคลเอนต์ได้รับสถานะเกมที่อัปเดตแล้วซึ่งสะท้อนการเดินหมากใหม่และการเปลี่ยนแปลงของเกม

ความเห็นของ GN⁺

  • โพสต์นี้พาไปดูสถาปัตยกรรมแบ็กเอนด์และกระบวนการที่ทำให้การเล่นแบบเรียลไทม์บน lichess.org ซึ่งเป็นแพลตฟอร์มหมากรุกโอเพนซอร์สยอดนิยมเกิดขึ้นได้อย่างละเอียด
  • เนื้อหาแนะนำองค์ประกอบทางเทคนิคสำคัญที่ควรพิจารณาเมื่อต้องสร้างเว็บแอปแบบเรียลไทม์ เช่น การสื่อสารแบบเรียลไทม์ด้วย WebSocket การส่งข้อความที่ขยายระบบได้ด้วย Redis Pub/Sub และการบันทึกข้อมูลถาวรด้วย MongoDB
  • สถาปัตยกรรมของ Lichess เหมาะมากกับเกมหลายผู้เล่นแบบเรียลไทม์ แต่รูปแบบและเทคนิคคล้ายกันก็สามารถนำไปใช้กับเว็บแอปเรียลไทม์ประเภทอื่นได้ด้วย เช่น แชต เครื่องมือทำงานร่วมกัน และฟีดโซเชียลมีเดีย
  • ฟีเจอร์เรียลไทม์ช่วยยกระดับประสบการณ์ผู้ใช้และการโต้ตอบได้ แต่ก็มาพร้อมความท้าทายทางเทคนิคเฉพาะตัว เช่น scalability, reliability และ data consistency โดยโพสต์นี้เสนอแนวทางรับมือกับความท้าทายเหล่านี้
  • ตัวอย่างโปรเจกต์โอเพนซอร์สที่ใช้เทคโนโลยีสแตกคล้ายกัน ได้แก่ Socket.IO (เฟรมเวิร์กสำหรับแอปเรียลไทม์บน Node.js) และ RethinkDB (ฐานข้อมูล NoSQL ที่เหมาะกับเว็บแอปเรียลไทม์)
  • การวิเคราะห์ในโพสต์นี้ไม่ได้ตรวจดูซอร์สโค้ดของ Lichess โดยตรง จึงอาจมีความแตกต่างจากการติดตั้งใช้งานจริง แต่แนวคิดพื้นฐานและรูปแบบสถาปัตยกรรมที่อธิบายไว้ยังคงใช้ได้
  • ในการออกแบบระบบเรียลไทม์ ควรพิจารณาอย่างรอบคอบว่าการส่งแบบ at-most-once (อาจเกิดการสูญหายของข้อความ) หรือ at-least-once (อาจเกิดข้อความซ้ำ) แบบใดเหมาะสมกว่า ทั้งนี้ขึ้นอยู่กับความต้องการและ trade-off ของแอปพลิเคชัน

1 ความคิดเห็น

 
GN⁺ 2024-10-24
ความเห็นจาก Hacker News
  • มีข้อร้องเรียนเกี่ยวกับโครงสร้างเวลาใน Chess.com ดูเหมือนว่าเซิร์ฟเวอร์จะเป็นฝ่ายติดตามเวลา จึงเหมือนไม่คำนึงถึงเวลาในการส่งข้อมูลและค่าหน่วง โดยเฉพาะเวลาที่เล่นเกมจับเวลาบนไคลเอนต์มือถือซึ่งทำให้ไม่สะดวกมาก

    • อาจเป็นปัญหาของโค้ดเครือข่ายด้วย และมักเกิดข้อผิดพลาดบ่อยในโหมดปริศนา
    • เทคโนโลยีของ Chess.com ให้ความรู้สึกยังไม่ค่อยเนี้ยบ
  • Lichess เลือกแนวทางแบบ StackOverflow และใช้เซิร์ฟเวอร์ที่ทรงพลัง

    • มีการบันทึกสถานะเกมเป็นระยะ แต่ไม่ชัดเจนว่าบันทึกไว้ที่ไหน
    • ต้นทุนต่อเกมต่ำมาก: $0.00027, 1 ดอลลาร์ต่อ 3,671 เกม
    • เคยเกิดเหตุขัดข้องนาน 10 ชั่วโมงจากการพึ่งพาศูนย์ข้อมูลเพียงแห่งเดียว
  • การคำนวณการเดินฝั่งเซิร์ฟเวอร์ช่วยรับประกันความสม่ำเสมอ และเพิ่มประสิทธิภาพการทำงานของไคลเอนต์ที่มีพลังประมวลผลหรือพลังงานจำกัด

    • อาจเป็นไปเพื่อให้การพัฒนาไคลเอนต์ซอฟต์แวร์โอเพนซอร์สบนแพลตฟอร์มใหม่มีอุปสรรคน้อยลง
    • การติดตั้งกฎหมากรุกอาจยุ่งยาก และ Lichess เองก็เคยมีข้อผิดพลาดทางตรรกะมาก่อน
  • ยังอธิบายไม่เพียงพอว่าจัดการกับข้อความสูญหายในช่อง Redis pub/sub อย่างไร

  • พารามิเตอร์ "l" อาจหมายถึงค่าหน่วงที่เซิร์ฟเวอร์สังเกตได้

  • น่าแปลกใจที่เซิร์ฟเวอร์จะไล่รายการการเดินถัดไปที่ถูกกฎหมายทั้งหมดแล้วส่งมา

    • อาจเป็นประโยชน์กับไคลเอนต์ที่มีข้อจำกัด แต่ก็ยังสงสัยว่าถูกกว่าการคำนวณฝั่งไคลเอนต์หรือไม่
  • มีคำถามเกี่ยวกับวิธีปกป้องเว็บซ็อกเก็ตเซิร์ฟเวอร์

    • หากใช้แพ็กเกจฟรีของ Cloudflare จะเกิดค่าหน่วง
    • อยากรู้ว่ามีโซลูชันฟรีอะไรบ้าง
  • สงสัยว่าเหตุใดโปรโตคอลจึงต้องมี ack

    • WebSocket ที่ห่อด้วย TLS สามารถรับประกันความสมบูรณ์ของข้อความได้
  • FEN เข้ารหัสเฉพาะสถานะของกระดาน ไม่รวมสถานะของเกม

    • โปรเจกต์ scalachess ที่เขียนด้วย Scala ยังคงได้รับการดูแลรักษาอย่างต่อเนื่อง