34 คะแนน โดย GN⁺ 2024-12-26 | 5 ความคิดเห็น | แชร์ทาง WhatsApp

Server-Sent Events (SSE) ถูกประเมินค่าต่ำเกินไป

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

ทำไม SSE จึงถูกประเมินค่าต่ำเกินไป

  • ความนิยมของ WebSocket: ความสามารถในการสื่อสารแบบ full-duplex ของ WebSockets ทำให้แนวทางที่เรียบง่ายของ SSE ถูกกลบไป
  • มุมมองต่อข้อจำกัด: แม้ลักษณะแบบทางเดียวอาจดูจำกัด แต่ก็เพียงพอสำหรับหลายกรณีการใช้งาน

จุดแข็งสำคัญของ SSE

  1. ความเรียบง่ายในการติดตั้งใช้งาน

    • ใช้ประโยชน์จากโปรโตคอล HTTP มาตรฐาน จึงตัดความซับซ้อนของการจัดการการเชื่อมต่อ WebSocket ออกไป
  2. ความเข้ากันได้กับโครงสร้างพื้นฐาน

    • ทำงานร่วมกับโครงสร้างพื้นฐาน HTTP ที่มีอยู่ได้อย่างราบรื่น:
      • load balancer
      • proxy
      • firewall
      • เซิร์ฟเวอร์ HTTP มาตรฐาน
  3. ประสิทธิภาพด้านทรัพยากร

    • ใช้ทรัพยากรน้อยกว่า WebSockets:
      • ธรรมชาติแบบทางเดียว
      • ใช้การเชื่อมต่อ HTTP มาตรฐาน
      • ไม่ต้องคอยดูแล socket แบบต่อเนื่อง
  4. การเชื่อมต่อใหม่อัตโนมัติ

    • มีการรองรับในตัวจากเบราว์เซอร์:
      • จัดการการหลุดของการเชื่อมต่อ
      • พยายามเชื่อมต่อใหม่อัตโนมัติ
      • มอบประสบการณ์เรียลไทม์ที่ยืดหยุ่น
  5. ความหมายเชิงโครงสร้างที่ชัดเจน

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

การใช้งานจริง

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

ตัวอย่างการติดตั้งใช้งาน

ฝั่งเซิร์ฟเวอร์ (Flask)
  • เส้นทาง /stream จัดการการเชื่อมต่อ SSE
  • generate_random_data() สร้างอีเวนต์ที่จัดรูปแบบแล้วอย่างต่อเนื่อง
  • MIME type text/event-stream ใช้ระบุโปรโตคอล SSE
  • stream_with_context ใช้คง application context ของ Flask ไว้
ฝั่งไคลเอนต์ (JavaScript)
  • อ็อบเจ็กต์ EventSource ใช้จัดการการเชื่อมต่อ SSE
  • handler onmessage ใช้ประมวลผลอีเวนต์ที่ได้รับ
  • onerror ใช้จัดการปัญหาการเชื่อมต่อ
  • เบราว์เซอร์จะจัดการการเชื่อมต่อใหม่อัตโนมัติ

ข้อจำกัดและสิ่งที่ต้องพิจารณา

  1. การสื่อสารทางเดียว

    • ทำได้เฉพาะจากเซิร์ฟเวอร์ไปยังไคลเอนต์
    • การสื่อสารจากไคลเอนต์ไปยังเซิร์ฟเวอร์ต้องใช้ HTTP request แยกต่างหาก
  2. การรองรับของเบราว์เซอร์

    • รองรับได้ดีในเบราว์เซอร์สมัยใหม่
    • เบราว์เซอร์รุ่นเก่าอาจต้องใช้ polyfill
  3. รูปแบบข้อมูล

    • รองรับข้อมูลแบบข้อความเป็นหลัก
    • ข้อมูลแบบไบนารีต้องมีการเข้ารหัส (เช่น Base64)

แนวทางปฏิบัติที่ดี

  • การจัดการข้อผิดพลาด

    • จัดการข้อผิดพลาดของการเชื่อมต่อด้วย eventSource.onerror
  • การจัดการการเชื่อมต่อ

    • ปิดและเก็บกวาดการเชื่อมต่อเมื่อเสร็จสิ้น
  • กลยุทธ์การเชื่อมต่อใหม่

    • กำหนดจำนวนครั้งสูงสุดในการลองใหม่และติดตั้งตรรกะการเชื่อมต่อใหม่

ตัวอย่างจริง: การติดตั้งใช้งานของ ChatGPT

  • โมเดลภาษาขนาดใหญ่สมัยใหม่ (LLM) ใช้ SSE เพื่อส่งคำตอบแบบสตรีม
  • รูปแบบหลัก:
    • ส่งคืนเฮดเดอร์ content-type: text/event-stream
    • สตรีมบล็อกข้อมูลที่คั่นด้วย \r\n\r\n

บทสรุป

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

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

 
eususu 2024-12-27

ผมได้นำ OpenAI ไปใช้งานผ่าน REST และได้ใช้ SSE จริง ๆ
ถ้าเป็นสถานการณ์ที่ต้องการการสื่อสารทางเดียว ผมคิดว่าจะเลือกใช้มันอย่างแน่นอน

 
galadbran 2024-12-27

SSE มักไม่ถูกบล็อกโดยอุปกรณ์ความปลอดภัย (เช่น web application firewall หรือระบบความปลอดภัยอัจฉริยะ) แต่ก็มักเจอกรณีที่สตรีมเป็นหน่วยตามอักขระขึ้นบรรทัดใหม่ไม่ได้ โดยเฉพาะในสภาพแวดล้อม (on-premises) ที่ตัวกลางจะรับ response ทั้งหมดไว้ก่อนแล้วค่อยส่งรวดเดียว

 
savvykang 2024-12-27

น่าเสียดายจริง ๆ ที่ OpenAPI ไม่รองรับ SSE

 
alska1039 2024-12-26

เป็นวิธีที่ดีมากในการสร้างการสื่อสารแบบสองทางในสภาพแวดล้อม NAT ครับ

 
GN⁺ 2024-12-26
ความคิดเห็นจาก Hacker News
  • Mercure เป็นโปรโตคอลเปิดที่อิงกับ SSE และใช้เป็นทางเลือกแทนโซลูชันที่อิงกับ WebSockets ได้ Mercure ทำงานโดยมีฮับแยกอิสระเป็นศูนย์กลางซึ่งคงการเชื่อมต่อ SSE แบบต่อเนื่องกับไคลเอนต์ไว้ และมี HTTP API แบบเรียบง่ายให้ทั้งแอปฝั่งเซิร์ฟเวอร์และไคลเอนต์ใช้งาน Mercure เพิ่มความสามารถอย่างกลไกยืนยันตัวตนแบบ JWT, การสมัครรับข้อมูลหลายหัวข้อผ่านการเชื่อมต่อเดียว, การบันทึกเหตุการณ์ และการปรับสถานะอัตโนมัติเมื่อเกิดปัญหาเครือข่าย

  • ข้อเสียใหญ่ของ SSE คือหากไม่ใช่ HTTP/2 จะมีข้อจำกัดเรื่องจำนวนการเชื่อมต่อสูงสุด ซึ่งอาจเป็นปัญหาได้เมื่อเปิดหลายแท็บ เพราะเบราว์เซอร์จำกัดจำนวนไว้ค่อนข้างต่ำ

  • มีการใช้ SSE ใน CLI ของ Doppler เพื่อทำฟีเจอร์รีสตาร์ตอัตโนมัติ โดยรับอีเวนต์จากเซิร์ฟเวอร์ผ่าน SSE แล้วดึงข้อมูลซีเคร็ตล่าสุดมาใส่ให้กับโปรเซสของแอปพลิเคชัน เหตุผลที่เลือก SSE แทน WebSockets คือเพื่อไม่ให้เพิ่ม dependency ในแอปพลิเคชัน Golang และต้องส่งอีเวนต์ "ping" เป็นระยะเพื่อแก้ปัญหา HTTP timeout

  • ลักษณะทางเดียวของ SSE อาจดูเหมือนมีข้อจำกัด แต่ในหลายกรณีก็เพียงพอแล้ว ข้อจำกัดหลักของ SSE คือรองรับเฉพาะข้อความเท่านั้น และมีข้อจำกัดการเชื่อมต่อของเบราว์เซอร์บน HTTP/1.1 หากใช้ HTTP/2 ขึ้นไป ข้อจำกัดเรื่องการเชื่อมต่อก็จะไม่เป็นปัญหา หากให้ความสำคัญกับประสิทธิภาพ สามารถใช้ fetch และ ReadableStream เพื่อเลือกโซลูชันที่ยืดหยุ่นกว่าและมี overhead น้อยกว่าได้

  • เพราะ SSE เรียบง่าย นักพัฒนาหลายคนจึงมักไม่ได้ใช้ implementation ที่เหมาะสม และหันไป parse data chunk ด้วย regular expression ซึ่งอาจเป็นปัญหาได้ เพราะ SSE รองรับคอมเมนต์ในสตรีม

  • Data-star.dev เป็นไลบรารีฝั่งฟรอนต์เอนด์ที่มุ่งเน้นการสตรีม hypermedia response ผ่าน SSE พัฒนาด้วยการใช้ Go และ NATS เป็นเทคโนโลยีแบ็กเอนด์ และเข้ากันได้กับทุก implementation ของ SSE

  • SSE ไม่ได้ถูกประเมินค่าต่ำเกินไป ที่จริง Open AI ก็ใช้มันกับการสตรีมผลลัพธ์ที่เสร็จสมบูรณ์ การนำ SSE ไปใช้ในโค้ดเบส ReactJS เป็นเรื่องยาก และในตอนนั้น Axios ยังไม่รองรับ จึงต้องใช้ native fetch

  • เมื่อมีการนำ SSE ไปใช้ในโปรเจ็กต์เว็บ พอเปิดเกิน 6 แท็บ เว็บไซต์ก็หยุดทำงาน Firefox นับการเชื่อมต่อ SSE รวมอยู่ในข้อจำกัดการเชื่อมต่อสูงสุด 6 รายการต่อโฮสต์ ทำให้คำขอเพิ่มเติมถูกบล็อก

  • SSE มักถูกประเมินค่าต่ำเกินไปตอนที่มันทำงานได้ดี แต่ในโปรเจ็กต์ที่กำลังทำอยู่ตอนนี้กลับเจอปัญหาเรื่องการยืนยันตัวตนและปัญหา keep-alive ของ tunnel ซึ่งไม่ใช่ปัญหาของโปรโตคอลเอง แต่การหาวิธีแก้ทำได้ยาก