Golang实现RabbitMQ中死信队列各个情况

下面这段教程针对是你已经有一些基本的MQ的知识,比如说能够很清楚的理解queue、exchange等概念,如果你还不是很理解,我建议你先访问官网查看基本的教程。

1、造成死信队列的主要原因

  • 消费者超时未应答
  • 队列的容量有限
  • 消费者拒绝了的消息

2、操作逻辑图

请添加图片描述

3、代码实战

其实整体的思路就是分别创建一个normal_exchange、dead_exchange、normal_queue、dead_queue,然后将normal_exchange与normal_queue进行绑定,将dead_exchange与dead_queue进行绑定,这里比较关键的一个点在于说如何将normal_queue与dead_exchange进行绑定,这样才能将错误的消息传递过来。下面就是这段代码的关键。

// 声明一个normal队列
	_, err = ch.QueueDeclare(
		constant.NormalQueue,
		true,
		false,
		false,
		false,
		amqp.Table{
			//"x-message-ttl":             5000,                    // 指定过期时间
			//"x-max-length":              6,						// 指定长度。超过这个长度的消息会发送到dead_exchange中
			"x-dead-letter-exchange":    constant.DeadExchange,    // 指定死信交换机
			"x-dead-letter-routing-key": constant.DeadRoutingKey,  // 指定死信routing-key
		})

3.1 针对原因1:消费者超出时间未应答

consumer1.go

package day07

import (
	amqp "github.com/rabbitmq/amqp091-go"
	"log"
	"v1/utils"
)

type Constant struct {
	NormalExchange   string
	DeadExchange     string
	NormalQueue      string
	DeadQueue        string
	NormalRoutingKey string
	DeadRoutingKey   string
}

func Consumer1() {
	// 获取连接
	ch := utils.GetChannel()
	// 创建一个变量常量
	constant := Constant{
		NormalExchange:   "normal_exchange",
		DeadExchange:     "dead_exchange",
		NormalQueue:      "normal_queue",
		DeadQueue:        "dead_queue",
		NormalRoutingKey: "normal_key",
		DeadRoutingKey:   "dead_key",
	}
	// 声明normal交换机
	err := ch.ExchangeDeclare(
		constant.NormalExchange,
		amqp.ExchangeDirect,
		true,
		false,
		false,
		false,
		nil,
	)
	utils.FailOnError(err, "Failed to declare a normal exchange")
	// 声明一个dead交换机
	err = ch.ExchangeDeclare(
		constant.DeadExchange,
		amqp.ExchangeDirect,
		true,
		false,
		false,
		false,
		nil,
	)
	utils.FailOnError(err, "Failed to declare a dead exchange")

	// 声明一个normal队列
	_, err = ch.QueueDeclare(
		constant.NormalQueue,
		true,
		false,
		false,
		false,
		amqp.Table{
			"x-message-ttl": 5000, // 指定过期时间
			//"x-max-length":              6,
			"x-dead-letter-exchange":    constant.DeadExchange,   // 指定死信交换机
			"x-dead-letter-routing-key": constant.DeadRoutingKey, // 指定死信routing-key
		})
	utils.FailOnError(err, "Failed to declare a normal queue")
	// 声明一个dead队列:注意不要给死信队列设置消息时间,否者死信队列里面的信息会再次过期
	_, err = ch.QueueDeclare(
		constant.DeadQueue,
		true,
		false,
		false,
		false,
		nil)
	utils.FailOnError(err, "Failed to declare a dead queue")

	// 将normal_exchange与normal_queue进行绑定
	err = ch.QueueBind(constant.NormalQueue, constant.NormalRoutingKey, constant.NormalExchange, false, nil)
	utils.FailOnError(err, "Failed to binding normal_exchange with normal_queue")
	// 将dead_exchange与dead_queue进行绑定
	err = ch.QueueBind(constant.DeadQueue, constant.DeadRoutingKey, constant.DeadExchange, false, nil)
	utils.FailOnError(err, "Failed to binding dead_exchange with dead_queue")

	// 消费消息
	msgs, err := ch.Consume(constant.NormalQueue,
		"",
		false, // 这个地方一定要关闭自动应答
		false,
		false,
		false,
		nil)
	utils.FailOnError(err, "Failed to consume in Consumer1")

	var forever chan struct{}

	go func() {
		for d := range msgs {
			if err := d.Reject(false); err != nil {
				utils.FailOnError(err, "Failed to Reject a message")
			}
		}
	}()
	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")

	<-forever
}

consumer2.go

package day07

import (
	amqp "github.com/rabbitmq/amqp091-go"
	"log"
	"v1/utils"
)

func Consumer2() {
	// 拿取信道
	ch := utils.GetChannel()

	// 声明一个交换机
	err := ch.ExchangeDeclare(
		"dead_exchange",
		amqp.ExchangeDirect,
		true,
		false,
		false,
		false,
		nil)
	utils.FailOnError(err, "Failed to Declare a exchange")

	// 接收消息的应答
	msgs, err := ch.Consume("dead_queue",
		"",
		false,
		false,
		false,
		false,
		nil,
	)

	var forever chan struct{}
	go func() {
		for d := range msgs {
			log.Printf("[x] %s", d.Body)
			// 开启手动应答ß
			d.Ack(false)
		}
	}()
	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	<-forever

}

produce.go

package day07

import (
	"context"
	amqp "github.com/rabbitmq/amqp091-go"
	"strconv"
	"time"
	"v1/utils"
)

func Produce() {
	// 获取信道
	ch := utils.GetChannel()
	// 声明一个交换机
	err := ch.ExchangeDeclare(
		"normal_exchange",
		amqp.ExchangeDirect,
		true,
		false,
		false,
		false,
		nil)
	utils.FailOnError(err, "Failed to declare a exchange")
	ctx, cancer := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancer()

	// 发送了10条消息
	for i := 0; i < 10; i++ {
		msg := "Info:" + strconv.Itoa(i)
		ch.PublishWithContext(ctx,
			"normal_exchange",
			"normal_key",
			false,
			false,
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        []byte(msg),
			})
	}
}

3.3 针对原因2:限制一定的长度

只需要改变consumer1.go中的对normal_queue的声明

// 声明一个normal队列
	_, err = ch.QueueDeclare(
		constant.NormalQueue,
		true,
		false,
		false,
		false,
		amqp.Table{
			//"x-message-ttl": 5000, // 指定过期时间
			"x-max-length":              6,
			"x-dead-letter-exchange":    constant.DeadExchange,   // 指定死信交换机
			"x-dead-letter-routing-key": constant.DeadRoutingKey, // 指定死信routing-key
		})

3.3 针对原因3:消费者拒绝的消息回到死信队列中

这里需要完成两点工作
工作1:需要在consumer1中作出拒绝的操作

go func() {
		for d := range msgs {
			if err := d.Reject(false); err != nil {
				utils.FailOnError(err, "Failed to Reject a message")
			}
		}
	}()

工作2:如果你consume的时候开启了自动应答一定要关闭

// 消费消息
	msgs, err := ch.Consume(constant.NormalQueue,
		"",
		false, // 这个地方一定要关闭自动应答
		false,
		false,
		false,
		nil)

其他的部分不需要改变,按照问题1中的设计即可。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值