Skip to main content

ระบบเฝ้าระวัง Dead-letter Queue (DLQ Monitor)

ภาพรวม

เมื่อ message ใดถูก retry ครบ 3 รอบแล้วยังล้มเหลว หรือเป็น permanent error ตั้งแต่แรก message นั้นจะถูก dead-letter ลง <queue>.dlq ปัญหาคือถ้าไม่มีใครดูแล message เหล่านั้นจะกองสะสมอยู่เงียบ ๆ โดยไม่มีใครรับรู้

DLQ Monitor คือ consumer พิเศษที่รันอยู่บน profile main ทำหน้าที่ subscribe DLQ ทุกตัว ที่อยู่ในชุดที่ต้อง monitor, log รายละเอียดของ message ที่ตายไว้ครบถ้วน (payload, x-death, x-retry-count) และรวบรวมเป็น batch เพื่อยิงแจ้งเตือนเข้า chat webhook แบบมี cooldown ป้องกันการ spam

Business Flow

  1. เมื่อ runMain เริ่มทำงาน ระบบจะสร้าง DLQMonitor แล้วรันเป็น goroutine แยกจาก registrar หลัก
  2. Monitor เปิด consumer 1 ตัวต่อ 1 DLQ ตามรายการใน MonitoredDLQQueues ซึ่งมี 37 queue (คือ AllQueues ทั้งหมดหักด้วย message_received_trigger ที่จงใจไม่ monitor)
  3. ทุกครั้งที่มี message ตกลง DLQ ระบบจะ
    • แกะและ log ชื่อ queue ต้นทาง, payload, error รวมถึง header x-death และ x-retry-count
    • เก็บรายการนั้นลง pendingAlerts โดยบันทึก queue, payload และ error
  4. หากเวลาผ่านไปเกิน DLQ_ALERT_COOLDOWN_MS (ค่าเริ่มต้น 5 นาที) นับจาก alert ครั้งก่อน ระบบจะรวม pendingAlerts ทั้งหมดยิงเป็นข้อความเดียวไปที่ DLQ_ALERT_WEBHOOK_URL แล้วเคลียร์ buffer
  5. Monitor จะ ack เสมอ ไม่ว่าจะ parse payload สำเร็จหรือไม่ เพื่อไม่ให้เกิด redelivery loop ที่ไม่มีวันจบใน DLQ

ไฟล์และฟังก์ชันหลัก

  • internal/mq/dlqmonitor.go
    • Client.NewDLQMonitor() — สร้าง monitor จาก config (DLQ_ALERT_WEBHOOK_URL, DLQ_ALERT_COOLDOWN_MS)
    • DLQMonitor.Run(ctx) — เปิด consumer ของทุก DLQ พร้อมกัน
    • DLQMonitor.consume(ctx, dlqQueue, suffix) — loop consume ต่อ 1 DLQ
    • dlqAlert — struct แทน alert หนึ่งรายการ
  • internal/mq/topology.goMonitoredDLQQueues() ซึ่งเป็นชุด queue ที่ถูก monitor
  • cmd/worker/main.go — ส่วนของ runMain() ที่สร้างและ start monitor
  • Queue ที่ subscribe คือ <queue>.dlq ทุกตัว โดย suffix มาจาก RABBITMQ_DLQ_SUFFIX (ค่าเริ่มต้น .dlq) และ bind อยู่กับ exchange line_exchange_dlq

จุดเชื่อมต่อกับ Service อื่น

  • RabbitMQ — consume จาก DLQ ทั้งหมด โดยต้อง assert topology ให้เรียบร้อยก่อน
  • Chat webhook (DLQ_ALERT_WEBHOOK_URL) — ยิง HTTP POST ด้วย body รูปแบบ {"text": ...} ซึ่งรองรับทั้ง Google Chat และ Slack โดยตั้ง client timeout ไว้ 5 วินาที หากไม่ได้ตั้งค่า URL นี้ monitor จะทำงานแบบ log อย่างเดียวโดยไม่ยิง alert
  • ไม่มีการเข้าถึงฐานข้อมูล — feature นี้เป็นงาน observability ล้วน ๆ
  • เกี่ยวข้องโดยตรงกับโครงสร้าง Queue RabbitMQ และกลไก Retry / Dead-letter