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

Pub/Sub

Redis Pub/Sub ทำตาม pattern publish-subscribe แบบคลาสสิก:

  1. Client เรียก SUBSCRIBE channel และเข้าสู่โหมดฟัง ไม่สามารถออกคำสั่งปกติได้อีกต่อไป — จะรับแต่ push message จาก Redis เท่านั้น
  2. Client อื่นใดเรียก PUBLISH channel message Redis จะส่งต่อข้อความนั้นไปยัง subscriber ที่เชื่อมต่ออยู่ทุกคนของ channel นั้นทันที
  3. ข้อความไม่เคยถูกเก็บไว้ หากไม่มี subscriber เชื่อมต่ออยู่ ข้อความจะถูกทิ้งอย่างเงียบ ๆ หาก subscriber เชื่อมต่ออยู่เมื่อสิบวินาทีก่อนแต่ตัดการเชื่อมต่อไปแล้ว จะไม่ได้รับอะไรเลย

แต่ละ channel เป็นแค่ชื่อ — Redis สร้างและลบ channel โดยปริยายเมื่อ subscriber มาและไป ไม่จำเป็นต้องสร้าง channel ก่อน publish

การโต้ตอบด้านล่างต้องใช้ session redis-cli สองอันที่รันพร้อมกัน เริ่ม Terminal 1 ก่อน แล้วส่งจาก Terminal 2

# Terminal 1 — subscriber
127.0.0.1:6379> SUBSCRIBE news
Reading messages... (press Ctrl-C to quit or any command to unsubscribe)
1) "subscribe"
2) "news"
3) (integer) 1
# Terminal 2 — publisher (in a second redis-cli session)
127.0.0.1:6379> PUBLISH news "Breaking: Redis is fast"
(integer) 1
# Back in Terminal 1 — message arrives automatically
1) "message"
2) "news"
3) "Breaking: Redis is fast"

ข้อความยืนยันที่ Redis ส่งกลับไปยัง subscriber เมื่อ SUBSCRIBE มีสามส่วน:

ส่วนค่าความหมาย
1"subscribe"ประเภท event
2"news"ชื่อ channel
3(integer) 1จำนวน channel ทั้งหมดที่ client นี้ subscribe อยู่ขณะนี้

ข้อความที่ publish มาถึงก็มีสามส่วนเช่นกัน: ประเภท event "message", ชื่อ channel และ payload

ตัวเลขที่ PUBLISH คืนกลับมาคือจำนวน subscriber ที่ได้รับข้อความ ค่าที่คืนมาเป็น 0 หมายความว่าไม่มีใครฟังอยู่

PUBLISH news "Breaking: Redis is fast"
PUBLISH news "Another update"

เพื่อออกจาก channel client ที่ subscribe อยู่จะส่ง UNSUBSCRIBE คุณสามารถระบุชื่อ channel เฉพาะหรือละเว้นชื่อ channel เพื่อ unsubscribe จากทุก channel พร้อมกันในครั้งเดียว

# Unsubscribe from one channel
127.0.0.1:6379> UNSUBSCRIBE news
1) "unsubscribe"
2) "news"
3) (integer) 0
# Unsubscribe from all channels (no argument)
127.0.0.1:6379> UNSUBSCRIBE
1) "unsubscribe"
2) (nil)
3) (integer) 0

เมื่อจำนวนการ subscribe ลดเหลือ 0 client จะออกจากโหมดฟังและสามารถออกคำสั่งปกติได้อีกครั้ง

การเรียก SUBSCRIBE ครั้งเดียวรับชื่อ channel ได้หลายชื่อ client จะรับข้อความจากทุก channel บน connection เดียวกัน

127.0.0.1:6379> SUBSCRIBE sports weather finance
1) "subscribe"
2) "sports"
3) (integer) 1
1) "subscribe"
2) "weather"
3) (integer) 2
1) "subscribe"
2) "finance"
3) (integer) 3

Redis ส่งการตอบกลับยืนยันหนึ่งครั้งต่อ channel ฟิลด์ที่สามจะเพิ่มขึ้นเมื่อเพิ่มการ subscribe แต่ละครั้ง แสดงผลรวมที่ทำงานอยู่

ตัวเลือกBenefitCost
Pub/Sub แบบ fire-and-forgetlatency ต่ำมาก ไม่มี overhead ของการเก็บ log ลงดิสก์ข้อความหายถาวรถ้าไม่มี subscriber เชื่อมต่ออยู่ตอนนั้น ไม่มีทาง replay
SUBSCRIBE หลาย channel บน connection เดียวลดจำนวน connection ที่ต้องเปิดกับ Redisclient อยู่ในโหมดฟังทั้ง connection รันคำสั่งอื่นบน connection เดียวกันไม่ได้จนกว่าจะ unsubscribe
  • ใช้ Pub/Sub ส่งข้อความที่ต้องรับประกันการส่งถึง — เช่น order confirmation หรือ payment event หาก consumer offline ชั่วครู่ข้อความจะหายไปเลย ควรใช้ Streams แทนเมื่อข้อความสำคัญห้ามสูญหาย
  • ลืมว่า client ที่ SUBSCRIBE อยู่ใช้คำสั่งทั่วไปไม่ได้ — connection จะเข้าสู่โหมดฟังทันที เรียก GET/SET บน connection เดียวกันไม่ได้ ต้องเปิด connection แยกสำหรับ publish
  • ไม่ตรวจสอบค่าที่ PUBLISH คืนมา — ค่า 0 แปลว่าไม่มี subscriber รับข้อความเลย ถ้าไม่เช็คค่านี้จะไม่รู้ตัวว่าข้อความหายไปเงียบ ๆ

💡 ตัวอย่างจากของจริง

แชทแอป — ใช้ Pub/Sub ส่งสถานะ “กำลังพิมพ์…” (typing indicator) เพราะข้อมูลนี้ล้าสมัยเร็วและยอมให้หายได้หาก subscriber ไม่ทัน

Dashboard แบบ real-time — ใช้ Pub/Sub กระจาย metric ที่อัปเดตทุกวินาทีไปยังหน้าเว็บที่เปิดอยู่หลายแท็บพร้อมกัน โดยไม่สนใจ event ที่พลาดไปเพราะค่าถัดไปจะมาเร็ว

`PUBLISH channel message` คืนค่าอะไร?
client รัน SUBSCRIBE news ไปแล้ว จะรัน GET mykey ต่อโดยไม่ unsubscribe ก่อนได้ไหม?
publisher ส่งข้อความไปยัง channel ที่มี subscriber ศูนย์คน จะเกิดอะไรขึ้นกับข้อความ?
ฟิลด์ตอบกลับใดใน push message ที่มีชื่อ channel?