nodejs+rabbitmq实现任务延迟执行与重试控制

以发送邮件任务为例,第一次发送邮件失败后,延时30s后继续发送此邮件,如果如此重试3次后仍然失败,则停止发送。

在这里插入图片描述
在这里插入图片描述

  • send_email: 发送邮件任务队列(死信队列)
  • send_email_retry: 发送邮件任务重试队列
  • send_email_fail: 发送邮件任务失败队列

在这里插入图片描述

生产者

  1. 创建send_email队列,并给队列绑定交换机exchange和路由routingkey
await ch.assertQueue(queue_send_email, {
        durable: true
    })
await ch.assertExchange(dlx_routing_exchange)
await ch.bindQueue(queue_send_email,dlx_routing_exchange, dlx_routing_key)
  1. 创建queue_retry队列,指定死信交换机deadLetterExchange和死信路由deadLetterRoutingKey以及队列消息过期时间messageTtl
    可以发现我们指定的queue_retry队列的死信交换机和死信路由和queue_email队列声明的交换机和路由一致。这样只要queue_retry队列中的消息过期后,就会自动从queue_retry队列转移到queue_email队列,而queue_email的消费者又可以消费了,这样就实现了延迟消费。而被死信交换机和死信路由绑定的队列(queue_email)就叫做 死信队列
await ch.assertQueue(queue_retry, {
        durable: true,
        deadLetterExchange: dlx_routing_exchange,
        deadLetterRoutingKey: dlx_routing_key,
        messageTtl: 30 * 1000
    })
  1. 创建queue_fail队列
    失败队列主要就是存储重试3次后仍然失败的发邮件任务,供人工查看。
await ch.assertQueue(queue_fail, {
        durable: true
    })
  1. 生产者创建发送邮件任务到queue_email队列
    注意: 在headers中指定了一个count参数,用来标识重试的次数
await ch.sendToQueue(queue, Buffer.from('发送邮件'), {
        deliveryMode: true,
        headers: {
            count
        }
    })

消费者

await ch.consume(queue, (msg) => {
         //获取重试的次数
        let retryCount = msg.properties.headers.count
        const result = sendEmail();
        if (result) { //发送成功
            console.log('发送成功')
        }
        if (!result && retryCount < 3) { //发送失败重试,且重试次数小于3
            //将消息发送到重试队列,并且重试次数+1
            sendToWorker(queue_retry, ++retryCount)
        } else {
            //将消息发送到失败队列
            sendToWorker(queue_fail, retryCount)
        }
        //不管是否发送成功能都要ack
        ch.ack(msg)
    }, {
        noAck: false
    })

完整代码

const amqp = require('amqplib');
const moment = require('moment')

var queue_send_email = 'send_email';
var queue_retry = 'send_email_retry'
var queue_fail = 'send_email_fail'
var dlx_routing_exchange = 'dlx_routing_exchange'
var dlx_routing_key = 'dlx_routing_key'

var conn = null

async function init() {
    await amqp.connect(`amqp://${user}:${password}@${host}:5672`).then(client => {
        conn = client
    })
}

function sendEmail() {
    return false
}

async function sendToWorker(queue, count) {
    // Publisher
    const ch = await conn.createConfirmChannel();
    await ch.assertQueue(queue_send_email, {
        durable: true
    })
    await ch.assertExchange(dlx_routing_exchange)
    await ch.bindQueue(queue_send_email,dlx_routing_exchange, dlx_routing_key)
    await ch.assertQueue(queue_retry, {
        durable: true,
        deadLetterExchange: dlx_routing_exchange,
        deadLetterRoutingKey: dlx_routing_key,
        messageTtl: 30 * 1000
    })

    await ch.assertQueue(queue_fail, {
        durable: true
    })
    await ch.sendToQueue(queue, Buffer.from('发送邮件'), {
        deliveryMode: true,
        headers: {
            count
        }
    })
    ch.close()
}

async function consumerByWorker(queue) {
    // Consumer
    const ch = await conn.createConfirmChannel();
    await ch.assertQueue(queue_send_email, {
        durable: true
    })
    await ch.assertExchange(dlx_routing_exchange)
    await ch.bindQueue(queue_send_email,dlx_routing_exchange, dlx_routing_key)
    await ch.prefetch(1)
    await ch.consume(queue, (msg) => {
        console.log(`${moment().format('HH:mm:ss')}--->发送邮件`)
        let retryCount = msg.properties.headers.count
        const result = sendEmail();
        if (result) { //发送成功
            console.log('发送成功')
        }
        if (!result && retryCount < 3) { //发送失败重试,且重试次数小于3
            console.log(`发送邮件,第${retryCount}次重试`)
            sendToWorker(queue_retry, ++retryCount)
        } else {
            console.log(`重试${retryCount}次后仍然失败,不再处理`)
            sendToWorker(queue_fail, retryCount)
        }
        //不管是否发送成功能都要ack
        ch.ack(msg)
    }, {
        noAck: false
    })
}

(
    async () => {
        await init()
        await consumerByWorker(queue_send_email)
        await sendToWorker(queue_send_email, 0)
    }
)()
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Selenium399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值