Flow Control & Alarms
ความไม่สมดุลพื้นฐาน
หัวข้อที่มีชื่อว่า “ความไม่สมดุลพื้นฐาน”queue จะแข็งแรงได้ก็ต่อเมื่อ consumer ตาม producer ทัน ในระยะยาว เมื่อ publisher วิ่งเร็วกว่า consumer อย่างต่อเนื่อง backlog จะโต, memory จะเต็ม และต้องมีอะไรสักอย่างยอมแตก RabbitMQ มีกลไกหลายอย่าง — ตั้งแต่นุ่มนวลไปถึงเด็ดขาด — เพื่อกัน broker ที่ overload ไม่ให้ crash
alarm block publisher (backpressure โดยตั้งใจ)
หัวข้อที่มีชื่อว่า “alarm block publisher (backpressure โดยตั้งใจ)”คุณเจอ alarm ไปแล้วในบทที่แล้ว นี่คือว่า รู้สึก ยังไง เมื่อ memory high-watermark หรือ limit ของ disk ว่างถูกข้าม RabbitMQ จะยก alarm และ block ทุก publishing connection publisher จะถูกหยุด — การเรียก publish จะไม่คืบหน้า — ขณะที่ consumer ยังไหล queue ออกต่อ
flowchart TB fast["Publisher (เร็วเกินไป)"] --> q["Queue เต็ม memory สูงขึ้น"] q --> alarm["ข้าม memory high-watermark → ALARM"] alarm --> block["publishing connection ถูก BLOCK"] cons["Consumer ยังไหล ออกต่อ"] --> q block --> recover["memory ลด → alarm เคลียร์ → publisher ทำงานต่อ"]
นี่คือ backpressure โดยตั้งใจ: แทนที่จะรับ message จนตัวเองตาย broker ดันแรงกดกลับไปที่ producer producer ที่ประพฤติดีจะสังเกตว่าถูก block (client มีสัญญาณ “connection blocked”) แล้วชะลอตัว ส่วนตัวที่ประพฤติไม่ดีก็แค่กอง publish ที่ถูก block ไว้ ไม่ว่าทางไหน broker ก็รอด
per-connection flow control
หัวข้อที่มีชื่อว่า “per-connection flow control”ก่อนจะถึง alarm ระดับ global ด้วยซ้ำ RabbitMQ ใช้ internal flow control ต่อ connection: ถ้า publisher ส่งเร็วกว่าที่ broker จะ route และ persist ได้ broker จะ throttle connection นั้นโดยเฉพาะเพื่อไม่ให้ publisher ถล่ม pipeline ภายใน ใน management UI connection แบบนี้จะแสดงสถานะ flow — เป็นสัญญาณปกติที่บอกว่า publisher นี้กำลังถูกจังหวะ ไม่ใช่ error
classic queue v2 และ backlog ขนาดใหญ่
หัวข้อที่มีชื่อว่า “classic queue v2 และ backlog ขนาดใหญ่”classic queue รุ่นเก่าพยายามเก็บ message ไว้ใน memory เป็นหลักเพื่อความเร็ว queue ที่ลึกมาก (message เป็นล้าน) จึงกิน RAM เยอะและอาจ trip memory alarm RabbitMQ เคยมี lazy queue mode แบบ opt-in (x-queue-mode: lazy) ให้ดัน message ไปเก็บบน disk แทน — แต่ตอนนี้ setting นั้นถูกเพิกเฉยแล้ว ตั้งแต่ RabbitMQ 3.12 classic queue version 2 (CQv2) เป็น storage engine default และมันทำงานแบบ lazy เดิมโดยอัตโนมัติอยู่แล้ว: backlog ที่ลึกส่วนใหญ่อยู่บน disk เหลือแค่ buffer เล็ก ๆ ใน memory สำหรับ message ที่กำลังจะถูก deliver classic queue สมัยใหม่จึงรับมือ backlog ขนาดใหญ่ได้ดีโดยไม่ต้อง opt-in อะไร ส่วน quorum queue เก็บบน disk เสมอและรับมือ backlog ลึก ๆ ได้ดีเช่นกัน
ออกแบบให้ alarm เกิดยาก
หัวข้อที่มีชื่อว่า “ออกแบบให้ alarm เกิดยาก”alarm และ flow control เป็นตาข่ายกันตก ไม่ใช่โหมดการทำงานปกติ ถ้าทำงานสม่ำเสมอ แปลว่าออกแบบผิด วิธีทำให้เกิดยาก:
- scale consumer ให้ ack rate เท่ากับ publish rate ในระยะยาว
- bound queue ด้วย
max-lengthหรือ TTL เพื่อไม่ให้ producer ที่หลุดควบคุมโต queue ได้ไม่จำกัด (drop หรือ dead-letter ส่วนที่ล้นแทน) - classic queue v2 จัดการเรื่องนี้ให้อยู่แล้ว — ตั้งแต่ RabbitMQ 3.12 ไม่มี lazy mode ให้ opt-in อีกต่อไป CQv2 เป็น default และเก็บ backlog ลึก ๆ ไว้บน disk โดยอัตโนมัติ ใช้ quorum queue เมื่อต้องการ replication ด้วย
- จัดการ “connection blocked” ใน producer — ถอยแทนที่จะกระหน่ำ
- alert เมื่อ queue depth ไต่ขึ้น เพื่อ scale consumer ก่อน memory alarm จะทำงาน