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

Fanout Exchange

fanout exchange เป็น router ที่ง่ายที่สุด: ไม่สนใจ routing key เลย และ copy message ทุกตัวไปยัง ทุก queue ที่ bind ไว้ publish ครั้งเดียว ได้ N queue เป็น copy อิสระ N ชุด

นี่คือวิธีทำ broadcast — หรือถ้ามองเป็น pattern ก็คือ publish/subscribe producer ประกาศ event หนึ่งครั้ง แล้ว subscriber ที่สนใจแต่ละตัวมี queue ของตัวเอง bind กับ exchange และรับ copy ของตัวเองไปประมวลผลอิสระ

เมื่อมีคนสั่ง order สามอย่างที่ไม่เกี่ยวกันต้องเกิดขึ้น: ส่ง email ยืนยัน, เขียน audit record และ invalidate cache ไม่มีอันไหนควร block อันอื่น และการเพิ่ม reaction ที่สี่ทีหลังไม่ควรต้องแตะ producer เลย fanout exchange เหมาะเป๊ะ

flowchart LR
  p["publish
'order.placed'"] --> x["fanout exchange
'orders'"]
  x --> qe["queue: email"]
  x --> qa["queue: audit"]
  x --> qc["queue: cache-invalidation"]
fanout exchange copy message เดียวไปทุก queue ที่ bind

consumer แต่ละตัวประกาศ queue ของตัวเอง แล้ว bind เข้ากับ fanout exchange เพราะแต่ละตัวมี queue แยกกัน subscriber ที่ช้าตัวหนึ่งจึงไม่หน่วงตัวอื่น และแต่ละตัวประมวลผลตาม pace ของตัวเอง

const ex = 'orders';
await channel.assertExchange(ex, 'fanout', { durable: true });
// each subscriber: its own queue, bound with NO routing key
const q = await channel.assertQueue('email', { durable: true });
await channel.bindQueue(q.queue, ex, ''); // key ignored for fanout
// producer: publish once; routing key is irrelevant
channel.publish(ex, '', Buffer.from(JSON.stringify({ orderId: 123 })));

ระวังอย่าสับสนกับ work queue (โมดูลถัดไป) ความต่างอยู่ที่ว่า queue อยู่ตรงไหน:

  • fanout / pub-sub: subscriber แต่ละตัวมี queue ของตัวเอง ทุกคนได้ ทุก message ใช้ broadcast event ไปยัง handler อิสระหลายตัว
  • work queue: worker หลายตัวใช้ queue เดียวร่วมกัน message แต่ละตัวไปหา worker แค่ตัวเดียว ใช้กระจาย task

broker เดียวกัน แต่เจตนาตรงข้าม และตัวชี้ขาดคือแค่ “ใช้ queue เดียวร่วมกัน หรือ queue ต่อ consumer หนึ่งตัว?”

fanout exchange route message อย่างไร?
จะ broadcast event ไปยัง handler อิสระหลายตัว subscriber แต่ละตัวควร:
อะไรแยก fanout/pub-sub ออกจาก work queue?