Kafka vs RabbitMQ vs BullMQ
เลือกเครื่องมือรับส่งข้อมูลและจัดการงานให้เหมาะกับธุรกิจ สถาปัตยกรรมระบบ และความสามารถของทีม
แนวทางเปิดการนำเสนอ:
วันนี้เราจะไม่ตัดสินจากคำถามว่าเครื่องมือใดเร็วที่สุดหรือได้รับความนิยมมากที่สุด แต่จะพิจารณาว่าแต่ละเครื่องมือถูกออกแบบมาเพื่อแก้ปัญหาอะไร
เป้าหมายของการนำเสนอคือทำให้เราตอบได้ว่า เมื่อใดควรใช้ Kafka, RabbitMQ หรือ BullMQ รวมถึงกรณีที่ลูกค้ามีเครื่องมือเดิมอยู่แล้ว
ทำไมระบบจึงไม่ควรทำทุกอย่างพร้อมกัน
ระบบแบบรอทุกขั้นตอน
- ลูกค้าต้องรอนานขึ้น
- ระบบหนึ่งล่มอาจทำให้กระบวนการทั้งหมดล้ม
- รองรับปริมาณงานช่วง Peak ได้ยาก
- ระบบต่าง ๆ ผูกติดกันมากเกินไป
ระบบแบบ Asynchronous
- ระบบหลักตอบลูกค้าได้เร็วขึ้น
- งานที่เหลือสามารถทำต่อเบื้องหลัง
- เพิ่ม Worker เพื่อรองรับปริมาณงานได้
- ระบบปลายทางล่มชั่วคราว งานยังรอได้
ยกตัวอย่างเมื่อลูกค้ากดสั่งซื้อ ระบบอาจต้องบันทึก Order, ตัด Stock, ชำระเงิน, ออก Invoice, ส่ง Email และส่งข้อมูลไป Analytics
ถ้า Order API ต้องรอทุกระบบ ลูกค้าจะรอนานและระบบจะเปราะบาง การมีตัวกลางทำให้ Order API รับรายการแล้วตอบกลับได้ก่อน ส่วนงานอื่นดำเนินการต่อภายหลัง
อย่างไรก็ตาม การทำงานแบบ Asynchronous ต้องออกแบบการติดตามสถานะ การ Retry และการจัดการข้อมูลซ้ำด้วย
จำง่าย ๆ: สมุดบันทึก ศูนย์คัดแยก และกระดานงาน
Apache Kafka
01
เปรียบเหมือน: สมุดบันทึกเหตุการณ์หรือกล้องวงจรปิด
ตอบคำถาม: “มีอะไรเกิดขึ้นแล้ว?”
เก็บเหตุการณ์ไว้ให้หลายระบบนำไปใช้และอ่านย้อนหลังได้
RabbitMQ
02
เปรียบเหมือน: ศูนย์คัดแยกพัสดุ
ตอบคำถาม: “ข้อความนี้ควรส่งไปที่ไหน?”
คัดแยกข้อความตามกฎแล้วส่งเข้า Queue ที่เหมาะสม
BullMQ
03
เปรียบเหมือน: กระดานมอบหมายงาน
ตอบคำถาม: “มีงานอะไรที่ Worker ต้องทำ?”
จัดคิวงานเบื้องหลัง พร้อม Retry, Delay และติดตามสถานะ
สไลด์นี้เป็นภาพจำหลักของการนำเสนอ หากผู้ฟังจำรายละเอียดทางเทคนิคไม่ได้ อย่างน้อยควรจำความแตกต่างสามประโยคนี้ได้
Kafka ไม่ได้เน้นว่าผู้รับคนใดต้องทำงาน แต่ประกาศว่าเหตุการณ์เกิดขึ้นแล้ว ส่วน RabbitMQ เน้นการนำข้อความไปส่งยังปลายทาง และ BullMQ เน้นสถานะของงานตั้งแต่รอทำจนสำเร็จหรือล้มเหลว
ก่อนเลือกเครื่องมือ ต้องแยกสิ่งที่กำลังส่งให้ชัดเจน
Event
ความหมาย: สิ่งที่เกิดขึ้นแล้ว
ผู้ส่งไม่สั่งว่าใครต้องทำอะไร
Message
ความหมาย: ข้อมูลที่ส่งระหว่างระบบ
เป็นคำกลางที่อาจหมายถึง Event หรือ Command
Command
ความหมาย: คำสั่งให้ผู้รับทำบางอย่าง
โดยทั่วไปควรมีผู้รับผิดชอบชัดเจน
Job
ความหมาย: งานที่ต้องทำให้เสร็จ
มักต้องติดตามสถานะ Retry และผลลัพธ์
Event ควรตั้งชื่อเป็นอดีตกาล เพราะเหตุการณ์เกิดขึ้นแล้ว เช่น OrderCreated ส่วน Command มักเป็นคำกริยาที่บอกให้ทำ เช่น CreateInvoice
Job คล้าย Command แต่เน้นมุมมองการบริหารงาน เช่น รอทำ กำลังทำ สำเร็จ ล้มเหลว หรือรอ Retry
การแยกคำเหล่านี้ให้ชัดช่วยป้องกันการนำเครื่องมือไปใช้ผิดบทบาท
เหตุการณ์เดียวกัน แต่แต่ละเครื่องมือมองต่างกัน
Kafka: กระจาย Event
แต่ละระบบอ่าน Event เดียวกันอย่างอิสระ
RabbitMQ: Route Message
Exchange ส่งข้อความเข้า Queue ตามกฎ
BullMQ: Execute Job
Worker รับงานไปทำ พร้อม Retry เมื่อผิดพลาด
กรณี Kafka ระบบ Order ประกาศ OrderCreated แล้ว Payment, Inventory และ Analytics เลือกอ่านได้เอง โดยแต่ละระบบไม่แย่งข้อมูลกันหากอยู่คนละ Consumer Group
กรณี RabbitMQ ระบบ Order ส่ง Message เข้า Exchange แล้ว Exchange ใช้ Routing Rule ส่งสำเนาไปยัง Queue ที่กำหนด
กรณี BullMQ แอปพลิเคชันสร้างงานเฉพาะ เช่น ส่ง Email หรือสร้าง PDF แล้ว Worker รับไปประมวลผล
เปรียบเทียบภาพรวมสำหรับผู้ตัดสินใจ
| หัวข้อ | Apache Kafka | RabbitMQ | BullMQ |
|---|---|---|---|
| ประเภทหลัก | Event Streaming Platform | Message Broker | Job Queue |
| จุดเด่น | เก็บและกระจาย Event | Routing ข้อความ | บริหาร Background Job |
| อ่านย้อนหลัง | เด่นมาก | ไม่ใช่จุดเด่น | ไม่ใช่เป้าหมายหลัก |
| Routing ซับซ้อน | ระดับ Topic/Key | เด่นมาก | ระดับ Queue/Job |
| Retry และ Job State | ต้องออกแบบเพิ่ม | Ack/Nack และ Dead Letter | มี Attempts/Backoff ในตัว |
| Technology Fit | หลายภาษา หลายระบบ | หลายภาษา หลาย Protocol | Node.js/TypeScript และ Redis |
| ความซับซ้อนเริ่มต้น | สูง | ปานกลาง | ต่ำถึงปานกลาง |
ตารางนี้ควรใช้เป็นภาพรวม ไม่ใช่คะแนนตัดสินขั้นสุดท้าย เพราะความเหมาะสมขึ้นอยู่กับ Requirement และสภาพแวดล้อมของลูกค้า
Kafka เด่นเรื่อง Replay และผู้บริโภคหลายกลุ่ม RabbitMQ เด่นเรื่อง Routing ส่วน BullMQ เด่นเรื่อง Developer Experience สำหรับ Background Job โดยเฉพาะใน Node.js
เหมาะเมื่อ Event คือสินทรัพย์ข้อมูลขององค์กร
แนวคิดสำคัญ
- ข้อมูลถูกบันทึกใน Topic
- Topic แบ่งเป็น Partition
- Consumer จดจำตำแหน่งด้วย Offset
- Consumer Group แบ่งงานหรืออ่านข้อมูลแยกกัน
- ข้อมูลเก็บตาม Retention Policy
Use Case ที่เหมาะ
- Enterprise Event Backbone
- Customer Activity Stream
- Transaction Event
- Change Data Capture
- Data Lake และ Analytics Pipeline
- Audit Trail และ Event Sourcing
ข้อควรระวัง
- Infrastructure และ Operation ซับซ้อน
- รับประกันลำดับภายใน Partition
- ต้องออกแบบ Partition Key ให้เหมาะสม
- Retry และ Dead Letter ต้องกำหนดมาตรฐาน
- ต้องบริหาร Schema Compatibility
ไม่ควรใช้เพียงเพราะ “มีอยู่แล้ว”
- ส่งอีเมลเพียงหนึ่งฉบับ
- สร้าง PDF เบื้องหลัง
- Resize รูปภาพ
- งานที่ต้องติดตาม Progress ราย Job
Kafka มองข้อมูลเป็น Log ที่ต่อเนื่อง Consumer แต่ละกลุ่มเก็บตำแหน่งการอ่านของตนเอง ทำให้ข้อมูลชุดเดียวกันถูกนำไปใช้ได้หลายวัตถุประสงค์
จุดสำคัญคือ Kafka ไม่ได้ทำให้ระบบง่ายขึ้นโดยอัตโนมัติ หากไม่มีทีมดูแล ไม่มีมาตรฐาน Schema หรือไม่มี Monitoring ความซับซ้อนอาจสูงกว่าประโยชน์
เหมาะเมื่อหัวใจของปัญหาคือการส่งและคัดแยกข้อความ
วิธีทำงาน
Exchange ใช้ Routing Key และ Binding เพื่อส่งข้อความไปยัง Queue ที่เหมาะสม
จุดแข็ง
- Routing ยืดหยุ่นและเข้าใจง่าย
- รองรับ Direct, Topic, Fanout และ Headers
- รองรับ Acknowledgement
- รองรับ Dead Letter
- เหมาะกับ Command และ Work Queue
Use Case ที่เหมาะ
- Payment และ Order Processing
- Routing ตามประเทศหรือประเภทงาน
- Command ระหว่าง Microservices
- กระจายงานไปยัง Worker
- เชื่อมระบบหลายภาษา
ข้อควรระวัง
- Queue ปกติไม่ใช่ Event History
- ต้องออกแบบ Retry ไม่ให้วนไม่สิ้นสุด
- ต้องจัดการ Poison Message
- Queue ที่ค้างมากอาจใช้ทรัพยากรสูง
RabbitMQ มี Exchange เป็นจุดรับข้อความ แล้วใช้กฎ Routing ส่งไปยัง Queue โดย Producer ไม่จำเป็นต้องรู้ว่า Consumer ตัวใดจะรับ
หากต้องการให้ Consumer หลายตัวช่วยกันทำงาน ให้ Consumer อ่าน Queue เดียวกัน แต่ถ้าต้องการให้หลายระบบได้รับข้อความคนละสำเนา ต้องสร้าง Queue แยกให้แต่ละระบบ
เหมาะกับ Background Job ของ Node.js Application
สถานะของ Job
จุดแข็ง
- เริ่มต้นได้เร็วใน Node.js/TypeScript
- รองรับ Retry และ Backoff
- รองรับ Delayed Job และ Priority
- ควบคุม Concurrency ได้
- รองรับ Rate Limiting
- ติดตามสถานะและ Progress ของ Job ได้
Use Case ที่เหมาะ
- ส่ง Email หรือ Notification
- สร้าง PDF และ Report
- Import/Export ไฟล์
- Resize รูปภาพ
- เรียก External API ที่จำกัด Request
ข้อควรระวัง
- Redis ต้องพร้อมสำหรับ Production
- ต้องวางแผน Persistence และ Memory
- ไม่ใช่ Enterprise Event History
- ไม่ควรเก็บ Completed Job โดยไม่จำกัด
- Job ต้องรองรับการทำซ้ำอย่างปลอดภัย
BullMQ เป็น Library ที่ใช้ Redis เป็นพื้นที่จัดเก็บสถานะ Queue และ Job จึงเหมาะอย่างมากเมื่อระบบเป็น Node.js และมี Redis ที่ดูแลอย่างเหมาะสมอยู่แล้ว
ข้อดีคือ Developer สามารถกำหนด Attempts, Backoff, Delay, Priority และ Rate Limit ได้สะดวก แต่ต้องไม่เข้าใจผิดว่า Redis ที่ใช้ Cache แบบทั่วไปจะพร้อมใช้เป็น Production Queue โดยอัตโนมัติ
ถ้าลูกค้ามีเครื่องมืออยู่แล้ว ควรตัดสินใจอย่างไร
มี Kafka อยู่แล้ว
ใช้ต่อเมื่อ:
- งานเป็น Business Event
- ต้อง Replay หรือมีหลาย Consumer
- ต้องส่งข้อมูลไป Analytics
พิจารณาตัวอื่นเมื่อ:
- เป็น Background Job แบบเฉพาะทาง
- ต้องติดตามสถานะราย Job
มี RabbitMQ อยู่แล้ว
ใช้ต่อเมื่อ:
- งานเป็น Message หรือ Command
- Routing ปัจจุบันตอบโจทย์
- ไม่ต้องอ่านประวัติย้อนหลัง
พิจารณา Kafka เมื่อ:
- ต้องสร้าง Event Backbone
- ต้องทำ Data Pipeline
มี BullMQ/Redis อยู่แล้ว
ใช้ต่อเมื่อ:
- เป็นงานภายใน Node.js
- เน้น Retry, Delay หรือ Rate Limit
- Redis มี HA และ Monitoring
พิจารณาตัวอื่นเมื่อ:
- เชื่อมหลาย Domain หรือหลายภาษา
- ต้องเก็บและ Replay Event
ก่อนเพิ่มเครื่องมือใหม่ ต้องประเมินต้นทุนรวม เช่น Infrastructure, Monitoring, Security, Training, On-call และ Disaster Recovery
ในทางกลับกัน การฝืนใช้ของเดิมกับงานที่ไม่เหมาะสมอาจสร้าง Technical Debt และ Operational Risk มากกว่าค่าใช้จ่ายในการเพิ่มเครื่องมือใหม่
คำตอบที่เหมาะสมจึงอาจเป็นการใช้ของเดิมต่อ หรือเพิ่มเครื่องมือใหม่เฉพาะส่วนโดยกำหนดขอบเขตให้ชัดเจน
ถ้ายังไม่มีเครื่องมือ ให้เริ่มจาก Decision Tree
อย่าเริ่มจากชื่อผลิตภัณฑ์ ให้เริ่มจากอายุของข้อมูล จำนวนผู้รับ รูปแบบ Routing ความต้องการ Replay และลักษณะของงาน
นอกจากนี้ต้องถามปริมาณเฉลี่ย ปริมาณช่วง Peak ขนาด Message ระยะเวลาที่ยอมให้ค้าง ความต้องการรักษาลำดับ และระดับ Disaster Recovery
เครื่องมือที่มีความสามารถสูงแต่ทีมดูแลไม่ได้ อาจเสี่ยงกว่าเครื่องมือที่เรียบง่ายกว่าแต่ตอบโจทย์ครบ
ไม่จำเป็นต้องเลือกเพียงตัวเดียว
Kafka
ประกาศ Business Event ให้หลายระบบรับรู้
BullMQ
บริหารงานส่ง Email/SMS พร้อม Retry และ Rate Limit
ขอบเขตที่ชัดเจน
Event ระดับองค์กรแยกจากงานภายใน Service
ตัวอย่างนี้ใช้ Kafka เป็น Event Backbone เมื่อเกิด OrderCreated หลายระบบสามารถรับรู้ได้ ส่วน Notification Service แปลง Event ให้เป็นงานส่ง Email หรือ SMS ใน BullMQ
การใช้หลายเครื่องมือไม่ใช่ Anti-pattern หากแต่ละตัวมีบทบาทและ Ownership ชัดเจน สิ่งที่ต้องหลีกเลี่ยงคือการใช้หลายเครื่องมือทำหน้าที่เดียวกันโดยไม่มีมาตรฐาน
ไม่ว่าจะเลือกตัวไหน สิ่งเหล่านี้ต้องออกแบบเสมอ
Idempotency
ข้อความเดิมเข้าซ้ำได้ แต่ต้องไม่เกิดผลทางธุรกิจซ้ำ
เช่น ห้ามตัดเงินหรือออก Invoice ซ้ำ
Retry Policy
กำหนดจำนวนครั้ง ระยะห่าง และ Error ที่ควร Retry
Dead Letter
แยกงานที่ล้มเหลวซ้ำออกมาตรวจสอบและ Reprocess
Observability
ติดตาม Queue Depth, Error Rate, Job Age และ Consumer Lag
Schema Governance
กำหนดรูปแบบข้อมูล Version และ Compatibility
Security
Authentication, Authorization, Encryption และ Audit Log
ระบบแบบ Distributed ไม่ควรตั้งสมมติฐานว่าข้อความจะเข้ามาเพียงครั้งเดียว การออกแบบ Idempotency จึงสำคัญมาก โดยเฉพาะ Payment, Stock และ Invoice
Monitoring ไม่ควรดูแค่ว่าระบบ Online หรือไม่ แต่ต้องดูอายุของ Message ที่เก่าที่สุด Queue Backlog, Retry Rate, Dead Letter และ Consumer Lag
ตัดสินใจแบบมืออาชีพด้วย Weighted Scoring
| เกณฑ์ | น้ำหนักตัวอย่าง | Kafka | RabbitMQ | BullMQ |
|---|---|---|---|---|
| เก็บและ Replay Event | 5 | 5 | 2 | 1 |
| Routing ซับซ้อน | 4 | 2 | 5 | 2 |
| Background Job | 4 | 2 | 4 | 5 |
| Data Pipeline | 5 | 5 | 2 | 1 |
| Delayed/Scheduled Job | 3 | 2 | 3 | 5 |
| เชื่อมหลายระบบและหลายภาษา | 4 | 5 | 5 | 3 |
| ใช้ Infrastructure เดิมได้ | 5 | ประเมิน | ประเมิน | ประเมิน |
| ทีมสามารถดูแลได้ | 5 | ประเมิน | ประเมิน | ประเมิน |
คะแนนในตารางเป็นเพียงตัวอย่าง ไม่ควรใช้ตัดสินใจโดยไม่ปรับน้ำหนักตามลูกค้า
ตัวอย่างเช่น ธนาคารอาจให้น้ำหนัก Reliability, Audit และ Security สูง ขณะที่ธุรกิจ Marketing อาจให้น้ำหนัก Time to Market และ Throughput สูงกว่า
ควรรวมต้นทุน Infrastructure, Development, Monitoring, Training, On-call, Migration และความเสียหายเมื่อระบบหยุดทำงานด้วย
อย่าตัดสินจาก Benchmark บนกระดาษเพียงอย่างเดียว
PoC ควรทดสอบ
- Normal Load และ Peak Load
- Consumer หรือ Worker ล่ม
- Broker หรือ Redis Node ล่ม
- Network ขาดช่วง
- Message ซ้ำหรือผิดรูปแบบ
- Poison Message และ Dead Letter
- Queue Backlog และ Consumer Lag
- Recovery และ Replay
ผลลัพธ์ที่ควรได้รับ
- Architecture Diagram
- Capacity Estimate
- Performance และ Failure Test
- Recovery Procedure
- Monitoring Dashboard
- Security Model
- Operational Runbook
- Cost Estimate และ Recommendation
Benchmark ต้องสะท้อนลักษณะงานจริง เช่น Message Size, Persistence, Replication, Acknowledgement, จำนวน Consumer และ Failure Recovery
PoC ที่ทดสอบเฉพาะกรณีระบบปกติยังไม่เพียงพอ ควรทดสอบว่าเกิดอะไรขึ้นเมื่อ Consumer ล่ม Network ขาด หรือ Queue สะสมจำนวนมาก
เป้าหมายไม่ใช่หาตัวเลขสูงที่สุด แต่คือยืนยันว่าระบบรองรับความต้องการจริงและทีมสามารถดูแลได้
เลือกเครื่องมือที่ง่ายที่สุด ซึ่งตอบ Requirement ได้ครบ
เลือกเมื่อ Event เป็นสินทรัพย์ข้อมูล
ต้องเก็บย้อนหลัง มีหลาย Consumer ต้อง Replay หรือส่งข้อมูลไป Data Platform
เลือกเมื่อหัวใจคือ Message และ Routing
ต้องส่ง Command ไปยังปลายทางที่เหมาะสม พร้อม Acknowledgement และ Dead Letter
เลือกเมื่อหัวใจคือ Background Job
เหมาะกับ Node.js/TypeScript ที่ต้องการ Retry, Delay, Priority, Rate Limit และ Job State
ใช้ร่วมกันเมื่อปัญหามีหลายประเภท
แบ่ง Event, Command และ Job ให้ชัดเจน พร้อมกำหนด Ownership และมาตรฐานการดูแล
บทพูดสรุป:
Kafka, RabbitMQ และ BullMQ ไม่ได้ทดแทนกันโดยตรงทั้งหมด Kafka เหมาะกับการเก็บและกระจายเหตุการณ์ RabbitMQ เหมาะกับการคัดแยกข้อความหรือคำสั่ง ส่วน BullMQ เหมาะกับการบริหารงานเบื้องหลังของแอปพลิเคชัน
หากลูกค้ามีเครื่องมือเดิม เราควรใช้ประโยชน์จากของเดิมก่อน ตราบใดที่ตอบ Requirement ได้จริง แต่หากลักษณะงานแตกต่างจากจุดแข็งของเครื่องมือเดิม การเพิ่มเครื่องมือที่เหมาะสมและแบ่งหน้าที่ชัดเจนจะลดความเสี่ยงในระยะยาว