ใน RabbitMQ เมื่อข้อความกลายเป็น dead letter (ข้อความที่ consumer ไม่สามารถประมวลผลได้) ในคิว ข้อความนั้นจะถูกเปลี่ยนเส้นทางไปยัง exchange อื่น ซึ่งเราเรียกว่า dead letter exchange และจากนั้น dead letter exchange จะส่ง dead letter ไปยัยคิว ซึ่งเราเรียกว่า dead letter queue.

ภาพประกอบของคิว dead letter

Dead Letter Queue

ภาพข้างต้นอธิบายกระบวนการทั้งหมดตั้งแต่การสร้าง dead letters จนถึงการจัดการด้วย

กระบวนการสร้าง Dead Letters

เงื่อนไขในการสร้าง dead letters มีดังนี้:

  • ข้อความถูกปฏิเสธโดย consumer อย่างด้อย (basic.reject / basic.nack) และ requeue = false.
  • ระยะเวลา (TTL) ของข้อความหมดอายุ
  • คิวมีข้อความเต็ม

ขั้นตอนการจัดการคิว Dead Letter

  1. กำหนด dead letter exchange (อย่างน้อยให้มันไม่คดเชื่อชื่อโกหการเรียก เพราะมันก็เป็น exchange ปกติเท่านั้น ค่ะ มันถูกเรียกตรงนั้นเฉพาะเฉพาะเรื่องการจัดการ dead letter เท่านั้น)
  2. กำหนดคิวที่จะ bind กับ dead letter exchange (คิวนี้มีชื่อว่า dead letter queue และก็เป็นคิวปกติด้วย)
  3. กำหนด consumer ในการบริโภค dead letter queue (อย่าหงุดเงิดกำหนดโดยชื่อ และก็เป็น consumer ปกติด้วย)
  4. บิด dead letter exchange กับคิวที่กำหนด (คิวที่ต้องการจัดการ dead letters ต้องถูก bind)

ทิป: อ้างถึงภาพข้างบนสำหรับหลักการ ภาษาโปรแกรมทุกภาษาจัดการคิว dead letter ในลักษณะเดียวกัน

การจัดการคิว Dead Letter ใน Golang

1. กำหนด Dead Letter Exchange

กำหนดมันเหมือนที่ exchange ปกติ

// ประกาศ exchange
err = ch.ExchangeDeclare(
    "tizi365.dead",   // ชื่อ exchange
    "topic", // ประเภทของ exchange
    true,     // ทนทาน
    false,
    false,
    false,
    nil,
)

2. กำหนด Dead Letter Queue

กำหนดมันเหมือนที่คิว ปกติ

    // ประกาศคิว
    q, err := ch.QueueDeclare(
        "",    // ชื่อคิว ปล่อยว่างไว้ให้มันสร้างขึ้นมาเอง
        false, // คิวทนทาน
        false,
        true,
        false,
        nil,
    )

    // Bind คิว กับ dead letter exchange
    err = ch.QueueBind(
        q.Name, // ชื่อคิว
        "#",     // รูทติ้งคีย์, # หมายถึงจับคู่ทุกรูทติ้ง, หมายถึงรับข้อความ dead letter ทั้งหมด
        "tizi365.dead", // ชื่อ dead letter exchange
        false,
        nil)

ทิป: จัดการคิว dead letter แบบปกติ

3. กำหนด Dead Letter Consumer

// สร้าง consumer
msgs, err := ch.Consume(
    q.Name, // อ้างถึงชื่อคิว dead letter ที่กำหนดไว้ก่อนหน้า
    "",     // ชื่อ consumer หากไม่ได้กำหนด เค้าจะสร้างขึ้นมาเอง
    true,   // การยินยอมโดยอัตโนมัติในการประมวลผลข้อความ
    false, 
    false, 
    false, 
    nil,
)

// การเปิดซองเพื่อบริโภคข้อความจากคิว dead letter
for d := range msgs {
    log.Printf("ได้รับข้อความ dead letter=%s", d.Body)
}

4. Bind Dead Letter Exchange กับคิวที่ระบุ

	// คุณสมบัติของคิว
	props := make(map[string]interface{})
	// Bind dead letter exchange
	props["x-dead-letter-exchange"] = "tizi365.dead"
	// ไม่บังคับ: กำหนด routing key เมื่อ dead letter ถูกส่งไปยัย dead letter exchange
	// ถ้าไม่กำหนด เค้าจะใช้ routing key ของข้อความเดิม
	// props["x-dead-letter-routing-key"] = "www.tizi365.com"

	q, err := ch.QueueDeclare(
		"tizi365.demo.hello", // ชื่อคิว
		true,   // ทนทาน
		false, 
		false, 
		false,   
		props,     // กำหนดคุณสมบัติของคิว
	)

โดยวิธีนี้ หากข้อความในคิว tizi365.demo.hello เป็น dead letters มันจะถูกส่งต่อไปยัย dead letter exchange tizi365.dead นี้เอง ค่ะ