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

Core Concepts

client คุยกับ RabbitMQ ผ่าน TCP connection เส้นเดียวที่อายุยาว แต่การเปิด TCP connection มีต้นทุนสูง และแอปที่ยุ่ง ๆ ต้องทำหลายอย่างพร้อมกัน — publish จากที่หนึ่ง, consume ที่อีกที่ และมีหลาย thread ทำงานขนานกัน

คำตอบคือ channel channel คือ virtual connection น้ำหนักเบาที่ multiplex อยู่บน TCP connection เดียว คุณเปิด หนึ่ง connection ต่อหนึ่ง process และ หลาย channel บนนั้น — โดยทั่วไปคือหนึ่ง channel ต่อ thread หรือต่อ task ที่ทำงานขนานกัน

flowchart LR
  app["Application"] --> conn["1 TCP Connection"]
  conn --> ch1["Channel 1
(publisher)"]
  conn --> ch2["Channel 2
(consumer)"]
  conn --> ch3["Channel 3
(consumer)"]
  ch1 --> broker["RabbitMQ"]
  ch2 --> broker
  ch3 --> broker
หนึ่ง connection หลาย channel

หลักจำง่าย ๆ: connection อายุยาวและมีน้อย; channel ถูกและมีเยอะ ความผิดพลาดที่พบบ่อยคือการเปิด connection ต่อทุก operation — นั่นทำให้ broker หมดแรง อีกอันคือการแชร์ channel เดียวข้ามหลาย thread — channel ไม่ thread-safe หนึ่ง connection ต่อแอป, หนึ่ง channel ต่อ worker

คุณเจอพวกนี้ในบทที่แล้ว นี่คือเวอร์ชันที่แม่นยำขึ้น:

  • exchange รับทุก message ที่ publish เข้ามา แล้วใช้ type และ binding ของตัวเองตัดสินใจว่า queue ไหนได้ message ไม่เก็บอะไรไว้เลย
  • queue คือ buffer ที่เรียงลำดับ ซึ่ง เก็บ message ไว้จนกว่า consumer จะมารับ นี่คือสิ่งเดียวที่เก็บ message
  • binding เชื่อม exchange เข้ากับ queue และอาจมี binding key ที่เมื่อรวมกับ routing key ของ message จะเป็นตัวกำหนดว่า message ตรงเงื่อนไขไหม

routing key ก็แค่ label ที่เป็น string ที่ producer ติดไว้บนแต่ละ message (เช่น order.created หรือ payment.failed) จะสำคัญไหมขึ้นกับ type ของ exchange — fanout exchange ไม่สนใจ routing key เลย ส่วน topic exchange เอา routing key มา pattern-match นั่นคือทั้งโมดูล Exchanges & Routing

รวมทุกอย่างเข้าด้วยกันแล้วไล่ตาม message ตัวเดียวจากต้นจนจบ:

sequenceDiagram
  participant P as Producer
  participant X as Exchange
  participant Q as Queue
  participant C as Consumer
  P->>X: publish(routing key, body)
  X->>Q: route ตาม binding
  Note over Q: message รออยู่ตรงนี้
  Q->>C: deliver
  C->>C: process
  C->>Q: ack (เสร็จแล้ว)
  Note over Q: message ถูกลบ
message จาก publish ถึง acknowledgement
  1. producer publish message พร้อม routing key ไปที่ exchange
  2. exchange route ไปยัง queue ที่ตรงเงื่อนไข (ศูนย์, หนึ่ง หรือหลาย queue)
  3. message รอ ใน queue จนกว่าจะมี consumer พร้อม
  4. queue deliver ให้ consumer
  5. consumer process แล้วส่ง acknowledgement — จากนั้น queue ถึงจะลบ message

step สุดท้ายคือตาข่ายกันตก: จนกว่า consumer จะ ack RabbitMQ ถือว่างานยังไม่เสร็จ ถ้า consumer crash กลางคัน message ที่ยังไม่ ack จะถูก redeliver (มีบทเรียนทั้งบทเรื่อง acknowledgement — นี่คือวิธีที่ “at-least-once” delivery ทำงาน)

channel ใน RabbitMQ คืออะไร?
การใช้ connection/channel ที่แนะนำคืออะไร?
component ตัวไหนที่เก็บ message จริง ๆ?
message ถูกลบออกจาก queue เมื่อไร?