Core Concepts
connection และ channel
หัวข้อที่มีชื่อว่า “connection และ channel”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 ต่อทุก operation — นั่นทำให้ broker หมดแรง อีกอันคือการแชร์ channel เดียวข้ามหลาย thread — channel ไม่ thread-safe หนึ่ง connection ต่อแอป, หนึ่ง channel ต่อ worker
exchange, queue และ binding
หัวข้อที่มีชื่อว่า “exchange, queue และ binding”คุณเจอพวกนี้ในบทที่แล้ว นี่คือเวอร์ชันที่แม่นยำขึ้น:
- 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 หนึ่งตัว
หัวข้อที่มีชื่อว่า “การเดินทางของ message หนึ่งตัว”รวมทุกอย่างเข้าด้วยกันแล้วไล่ตาม 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 ถูกลบ
- producer publish message พร้อม routing key ไปที่ exchange
- exchange route ไปยัง queue ที่ตรงเงื่อนไข (ศูนย์, หนึ่ง หรือหลาย queue)
- message รอ ใน queue จนกว่าจะมี consumer พร้อม
- queue deliver ให้ consumer
- consumer process แล้วส่ง acknowledgement — จากนั้น queue ถึงจะลบ message
step สุดท้ายคือตาข่ายกันตก: จนกว่า consumer จะ ack RabbitMQ ถือว่างานยังไม่เสร็จ ถ้า consumer crash กลางคัน message ที่ยังไม่ ack จะถูก redeliver (มีบทเรียนทั้งบทเรื่อง acknowledgement — นี่คือวิธีที่ “at-least-once” delivery ทำงาน)