11 คะแนน โดย GN⁺ 2025-04-13 | 3 ความคิดเห็น | แชร์ทาง WhatsApp
  • WebSocket มีประโยชน์สำหรับการสื่อสารแบบเรียลไทม์ แต่ไม่ได้จำเป็นเสมอไป และทางเลือกที่อิงกับ HTTP อาจเรียบง่ายและเสถียรกว่า
  • ในด้านการจัดการทรานแซกชัน การดูแลการเชื่อมต่อ และความซับซ้อนของเซิร์ฟเวอร์ WebSocket อาจก่อให้เกิด overhead มากเกินความจำเป็น
  • การใช้ HTTP Streaming และไลบรารี eventkit ทำให้สามารถซิงก์แบบเรียลไทม์และจัดการอีเวนต์ได้โดยไม่ต้องพึ่ง WebSocket

WebSocket คืออะไร

  • WebSocket คือเทคโนโลยีที่เปิดช่องทางการสื่อสารแบบสองทางอย่างต่อเนื่องระหว่างไคลเอนต์และเซิร์ฟเวอร์
  • แม้จะเริ่มต้นการเชื่อมต่อผ่าน HTTP แต่หลังจากนั้นการสื่อสารจะเกิดขึ้นผ่านโปรโตคอลอีกแบบหนึ่ง
  • มักถูกใช้ในการสร้างแอปพลิเคชันแบบเรียลไทม์ และมีประโยชน์ตรงที่รองรับการสื่อสารสองทาง

ข้อความของ WebSocket ไม่ได้ทำงานแบบทรานแซกชัน

  • WebSocket ไม่ได้การันตีความสัมพันธ์โดยตรงระหว่างคำขอกับคำตอบ
  • คำสั่งเปลี่ยนสถานะและข้อความผลลัพธ์ของมันอาจถูกส่งมาปะปนกันในสตรีมเดียวกัน
  • ตัวอย่างเช่น หากไคลเอนต์ตัวหนึ่งเปลี่ยนสถานะแล้วเกิดข้อผิดพลาด ก็อาจระบุได้ยากว่าข้อผิดพลาดนั้นเป็นของคำสั่งใด
  • วิธีแก้คือใส่ requestId เพื่อเชื่อมคำสั่งกับคำตอบเข้าด้วยกัน แต่ก็ทำให้ความซับซ้อนและต้นทุนในการดูแลเพิ่มขึ้น
  • แนวทางที่ง่ายกว่าคือใช้วิธีแบบทรานแซกชันผ่าน HTTP สำหรับส่งคำสั่ง และใช้ WebSocket เฉพาะการกระจายการเปลี่ยนแปลงสถานะ
  • สามารถแยกฝั่งส่งเป็น HTTP request และฝั่งรับเป็น WebSocket หรือวิธีสตรีมมิงแบบอื่นได้

ความยากในการจัดการวงจรชีวิตการเชื่อมต่อของ WebSocket

  • เมื่อใช้ WebSocket คุณต้องจัดการการเริ่มต้น การสิ้นสุด ข้อผิดพลาด และการเชื่อมต่อใหม่ด้วยตัวเอง
  • ตัวอย่างการจัดการพื้นฐานในเบราว์เซอร์จะรวมถึงการเปิดการเชื่อมต่อ การรับข้อความ การเกิดข้อผิดพลาด และการจัดการอีเวนต์ตอนการเชื่อมต่อปิดลง
  • ยังต้องมีลอจิกเพิ่มเติม เช่น การเชื่อมต่อใหม่ การบัฟเฟอร์ข้อความ และ exponential backoff
  • ในทางกลับกัน HTTP มีจุดเริ่มต้นและจุดสิ้นสุดที่ชัดเจนในระดับคำขอ ทำให้พัฒนาได้ง่ายกว่า
  • การจัดการวงจรชีวิตที่ซับซ้อนแบบนี้จะสมเหตุสมผลก็ต่อเมื่อมีเหตุผลชัดเจนพอที่จะใช้ WebSocket

ความซับซ้อนของโค้ดฝั่งเซิร์ฟเวอร์ที่เพิ่มขึ้น

  • WebSocket ต้องรองรับคำขออัปเกรดจาก HTTP ซึ่งต้องมีลอจิก handshake เพิ่มเติม
  • ต้องตรวจสอบ header พิเศษอย่าง Sec-WebSocket-Key และส่ง response header กลับอย่างเหมาะสม
  • หลังเชื่อมต่อ WebSocket แล้ว ยังต้องคงสถานะการรับส่งข้อความอย่างต่อเนื่อง และอาจเจอปัญหาอย่างการจัดการ partial frame
  • เมื่อเทียบกับการใช้ HTTP อย่างเดียว การดีบักและการจัดการข้อผิดพลาดจะยากขึ้น
  • แม้เฟรมเวิร์กจะช่วย abstract บางส่วนออกไปได้ แต่ความซับซ้อนพื้นฐานก็ยังคงอยู่

ทางเลือก: HTTP Streaming

  • HTTP เป็นโปรโตคอลที่รองรับการสตรีมมิงมาแต่เดิม จึงส่งข้อมูลเป็นสตรีมแบบเรียลไทม์ได้โดยไม่ต้องรอทั้งไฟล์ครบก่อน
  • สามารถแทนที่เฉพาะความสามารถฝั่งรับของ WebSocket เดิมด้วย HTTP streaming ได้
  • ใช้ asynchronous generator เพื่อจัดการการอัปเดตสถานะในรูปแบบสตรีมได้
  • โฟลว์ฝั่งเซิร์ฟเวอร์
    • การอัปเดตสถานะจะเกิดขึ้นในฟังก์ชันประมวลผลคำสั่ง
    • ไคลเอนต์ที่เชื่อมต่ออยู่จะได้รับค่าที่อัปเดตผ่าน generator ทุกครั้งที่มีค่าใหม่ออกมา
    • คำสั่งเปลี่ยนสถานะจะถูกส่งผ่าน HTTP POST และสมัครรับสตรีมแบบเรียลไทม์ผ่านคำขอ GET
  • โฟลว์ฝั่งไคลเอนต์
    • รับข้อมูลแบบเรียลไทม์ผ่าน Fetch API และ Stream Reader
    • ถอดรหัสข้อความแล้วอัปเดต UI
  • โครงสร้างนี้ทำให้สามารถซิงก์สถานะแบบเรียลไทม์ได้โดยไม่ต้องใช้ WebSocket

โบนัส: แนะนำไลบรารี eventkit

  • eventkit เป็นไลบรารีที่ช่วยให้สร้างและสังเกต asynchronous stream ได้ง่าย
  • คล้ายกับ RxJS แต่ปรับปรุงเรื่องการจัดการ side effect และออกแบบบนพื้นฐานของ generator
  • เมื่อ push การอัปเดตสถานะเข้าไปในสตรีม ไคลเอนต์ก็จะรับข้อมูลนั้นแบบเรียลไทม์ได้
  • สามารถใช้งานได้อย่างเรียบง่ายทั้งฝั่งเซิร์ฟเวอร์และไคลเอนต์ผ่าน Stream และ AsyncObservable
  • การใช้ eventkit ฝั่งเซิร์ฟเวอร์
    • push การเปลี่ยนแปลงสถานะเข้าไปใน Stream แล้วให้ไคลเอนต์ subscribe สตรีมนั้น
  • การใช้ eventkit ฝั่งไคลเอนต์
    • รับข้อมูลจากสตรีม ถอดรหัส แล้วอัปเดต UI
  • มีทั้งคลัง GitHub อย่างเป็นทางการและคู่มือ HTTP Streaming ให้ใช้งาน

GitHub: https://github.com/hntrl/eventkit

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

 
[ความคิดเห็นนี้ถูกซ่อน]
 
[ความคิดเห็นนี้ถูกซ่อน]
 
GN⁺ 2025-04-13
ความคิดเห็นบน Hacker News
  • คิดว่า HTTP streaming ไม่ได้ถูกออกแบบมาโดยคำนึงถึงแพตเทิร์นนี้ HTTP streaming มีไว้สำหรับแบ่งข้อมูลก้อนใหญ่ออกเป็นชิ้น ๆ หากใช้การสตรีมเหมือนกลไก pub/sub ก็อาจต้องมาเสียใจภายหลัง ตัวกลางของ HTTP ไม่ได้คาดการณ์แพตเทิร์นทราฟฟิกแบบนี้ไว้ (เช่น NGINX, CloudFlare) ทุกครั้งที่การเชื่อมต่อ WiFi หลุด fetch API ก็น่าจะโยนข้อผิดพลาดว่า request ล้มเหลว

    • หลายกรณีไม่จำเป็นต้องใช้ WebSockets เลย Server-Sent Events (SSE) เป็นทางออกที่ง่ายกว่า น่าเสียดายที่ SSE ไม่ค่อยได้รับความสนใจ
  • การส่ง RequestID ไปยังเซิร์ฟเวอร์เพื่อให้ได้วงจร request/response ไม่ใช่เรื่องแปลกหรือเกินจำเป็น สำหรับแอปจริงจัง การมี API แบบ send(message).then(res => ...) นั้นคุ้มค่าเสมอ

    • คำขอ upgrade ทำให้งงอยู่บ้าง น่าหงุดหงิดที่เซิร์ฟเวอร์เว็บซ็อกเก็ตถูกฝังอยู่ในเซิร์ฟเวอร์ HTTP และไม่ผสานรวมกัน
    • แทนที่จะนำมิดเดิลแวร์ที่อ่าน headers['authorization'] จากคำขอเว็บซ็อกเก็ตกลับมาใช้ซ้ำ กลับต้องไปเข้าถึงออบเจ็กต์ connectionParams ที่ทำตัวเหมือนเป็น request header
    • WebSockets Browser API ใช้งานง่ายกว่า EventSource
  • การสตรีมวิดีโอคือไคลเอนต์ร้องขอชังก์ตามช่วงข้อมูล ไม่ใช่การเชื่อมต่อ HTTP เดียว

  • ควรใช้ SSE แทน EventKit

  • ใน POC จะใช้การส่งฟอร์ม HTTP แบบดั้งเดิม ไม่ต้องการอย่างอื่น

    • สถาปนิกยืนกรานว่าต้องใช้เว็บซ็อกเก็ต
    • POC ไม่ต้องใช้ทั้ง XHR หรือเว็บซ็อกเก็ต เป็นโฟลว์การซื้อแบบลำดับขั้น
    • สุดท้ายก็ต้องส่งมอบเว็บซ็อกเก็ตที่ไม่จำเป็น
  • ปัญหาของ HTTP/2 คือ server push เป็นสิ่งที่ถูกเพิ่มทับลงบนโปรโตคอลเดิม HTTP เป็นโปรโตคอลสำหรับส่งทรัพยากร จึงเพิ่มโอเวอร์เฮดที่ไม่จำเป็น เป้าหมายหลักของ HTTP/2 คือให้เซิร์ฟเวอร์ push ไฟล์/ทรัพยากรไปยังไคลเอนต์ล่วงหน้าเพื่อลด round-trip latency

    • WebSockets เป็นโปรโตคอลที่ง่ายกว่าซึ่งถูกออกแบบมาสำหรับการสื่อสารสองทาง ควบคุมการไหลของข้อมูลผ่านการเชื่อมต่อเดียวได้ง่าย จัดการสถานะและกู้คืนจากการเชื่อมต่อหลุดได้สะดวก การยืนยันตัวตนและการควบคุมสิทธิ์ก็ง่ายขึ้น
  • WebSockets ไม่ได้ส่งเป็นสตรีม แต่ส่งเป็น datagram (packet) WebSockets API ของไลบรารี JavaScript ไม่สามารถจัดการ backpressure ได้ และจัดการข้อผิดพลาดได้ไม่ครบทั้งหมด หากจะใช้เหมือนเป็น TCP stream ก็ต้องระวัง

  • รู้สึกเสียดายหลังจากนำ WebSockets ไปใช้ในโปรดักชัน มีปัญหาอย่าง NGINX ปิดการเชื่อมต่อหลัง 4/8 ชั่วโมง และเบราว์เซอร์ไม่เชื่อมต่อใหม่หลังจากเครื่องตื่นจาก sleep เป็นต้น ถ้าเป็นไปได้ควรหลีกเลี่ยง WebSockets และการเชื่อมต่อระยะยาว

  • มีภาพจำในอุดมคติเกี่ยวกับ WebSockets อยู่ หลายคนมักโน้มเอียงจะใช้ WebSockets กับกรณีใช้งานแบบสตรีมหรือเรียลไทม์ WebSockets ทำให้สูญเสียความเรียบง่ายและข้อดีของเครื่องมือในฝั่ง HTTP ทางออกสำหรับการเปลี่ยนแปลงฝั่งเซิร์ฟเวอร์แบบสตรีมคือ h2/h3 และ SSE หากสามารถทำแบตช์ได้ไม่เกิน 0.5 req/s ต่อไคลเอนต์ ก็ไม่จำเป็นต้องใช้ WebSockets

  • คนที่สนใจ HTTP streaming ควรไปดู Braid-HTTP ซึ่งขยาย HTTP สำหรับ event streaming ได้อย่างสวยงาม และให้โปรโตคอลซิงก์สถานะที่ทรงพลัง