ข้ามไปยังเนื้อหา

Streaming

ทุก gRPC call วิ่งบน HTTP/2 stream เส้นเดียว และเพราะ stream นั้นส่ง message เป็น ลำดับ ได้ทั้งสองทิศทาง gRPC จึงมี call ให้เลือก 4 แบบ — ไม่ใช่ transport 4 อัน แค่รูปแบบการใช้ stream เดียวกัน 4 แบบ

flowchart TB
  subgraph u["Unary"]
    direction LR
    uc["client"] -- "1 request" --> us["server"]
    us -- "1 response" --> uc
  end
  subgraph ss["Server streaming"]
    direction LR
    sc["client"] -- "1 request" --> sss["server"]
    sss -- "many responses" --> sc
  end
  subgraph cs["Client streaming"]
    direction LR
    csc["client"] -- "many requests" --> css["server"]
    css -- "1 response" --> csc
  end
  subgraph bd["Bidirectional"]
    direction LR
    bc["client"] <-- "many both ways" --> bs["server"]
  end
gRPC call ทั้ง 4 แบบ

unary คุณรู้จักอยู่แล้ว — 1 request 1 response คือ RPC ที่ใช้ทุกวัน โมดูลนี้ครอบคลุมอีก 3 แบบที่เหลือ พร้อม flow control ที่ทำให้ stream ทำงานได้ราบรื่น

บทเรียนรูปแบบเหมาะกับ
Server streaming1 request → N responseslive feed, result set ขนาดใหญ่, progress update
Client streamingN requests → 1 responseupload, batching, การรับ metric
Bidirectional streamingN ↔ Nchat, multiplayer, session แบบโต้ตอบ
Flow control & backpressureกัน producer ที่เร็วไม่ให้ท่วม consumer ที่ช้า

ใช้ streaming เมื่อ:

  • result set ใหญ่หรือไม่จำกัด — ส่ง 10,000 row เป็น stream เดียวช่วยไม่ต้อง buffer ทั้งหมดไว้ใน memory และ client เริ่มประมวลผล row แรกได้ก่อนที่ row สุดท้ายจะถูกสร้าง
  • data ทยอยมาตามเวลา — price, event, log สั่งให้ server push แต่ละ item ทันทีที่เกิด แทนที่ client จะ poll
  • อยากได้ call ยาว 1 call แทนการ setup หลายครั้ง — stream เฉลี่ย overhead ต่อ call ไปกับ message จำนวนมาก

streaming ไม่ฟรี และการหยิบมาใช้แบบอัตโนมัติเป็นความผิดพลาดที่พบบ่อย:

  • request/response เดียวเรียบง่ายกว่า ถ้ามี input เดียว output เดียว ใช้ unary เถอะ stream เพิ่ม lifecycle, การ handle error ที่เกิดได้ทุกจุด และ retry ที่ยากขึ้น
  • stream มี state และอยู่ยาว ซึ่งทำให้ load balancing ยุ่งยาก — stream ถูกผูกกับ backend เดียวตลอดอายุของตัวเอง rebalance กลางทางไม่ได้
  • retry ยากขึ้น unary call retry ง่ายมาก แต่ stream ที่บริโภคไปครึ่งทางแล้ว retry ไม่ได้ง่าย ๆ
gRPC call ทั้ง 4 แบบมีอะไรร่วมกันจริง ๆ?
เหตุผลที่หนักแน่นที่สุดในการเลือก streaming แทน unary call ซ้ำ ๆ คืออะไร?
ทำไม streaming ถึงทำให้ load balancing ยุ่งยาก?