一文搞懂 RabbitMQ 延时队列(订单定时取消为例)

1. 死信及死信队列

1.1 什么是死信

一般来说,生产者将消息投递到队列中,消费者从队列取出消息进行消费,但某些时候由于特定的原因导致队列中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信(Dead Letter),所有的死信都会放到死信队列中。

为什么为有死信?消息变成死信一般是以下三种情况:

  1. 消息被拒绝,即basicReject/basicNack,并且设置 requeue 参数为 false,这种情况一般消息丢失 。
  2. 消息过期(TTL),TTL全称为Time-To-Live,表示的是消息的有效期,默认情况下 Rabbit 中的消息不过期,但是可以设置队列的过期时间和消息的过期时间以达到消息过期的效果 ,消息如果在队列中一直没有被消费并且存在时间超过了TTL,消息就会变成了"死信" ,后续无法再被消费。
  3. 队列达到最大长度,一般当设置了最大队列长度或大小并达到最大值时。

1.2 死信交换器 DLX

在消息的拒绝操作都是在requeue = true情形下,如果为 false 可以发现当发生异常确认后,消息丢失了,这肯定是不能容忍的,所以提出了死信交换器(dead-letter-exchange)的概念。

死信交换器仍然只是一个普通的交换器,创建时并没有特别要求和操作。在创建队列的时候,声明该交换器将用作保存被拒绝的消息即可,相关的参数是 x-dead-letter-exchange。当这个队列中有死信时,RabbitMQ 就会自动的将这个消息重新发布到设置的 Exchange 上去,进而被路由到另一个队列。

举个栗子

1、生产者生产 3 条消息

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class DlxProducer {
   

    public final static String EXCHANGE_NAME = "dlx_exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
   
        //建立连接
        Connection connection = RabbitMQUtils.getConnection();
        // 创建一个信道
        Channel channel = connection.createChannel();
        // 指定转发
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        String[] routekeys = {
   "rabbit", "cat", "dog"};
        for (int i = 0; i < 3; i++) {
   
            String routekey = routekeys[i % 3];
            String msg = "Hello,RabbitMq" + (i + 1);
            channel.basicPublish(EXCHANGE_NAME, routekey, null, msg.getBytes());
            System.out.println("Sent " + routekey + ":" + msg);
        }
        // 关闭频道和连接
        channel.close();
        connection.close();
    }
}

2、普通消费者消费消息,但是不能消费全部的消息,并把不能消费得消息投递到死信队列。如果是我们还想做点其他事情,我们可以在死信交换的时候改变死信消息的路由键,具体的相关的参数是 x-dead-letter-routing-key

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

/**
 * 类说明:普通的消费者,但是自己无法消费的消息,将投入死信队列
 */
public class NormalDlxConsumer {
   

    public static void main(String[] args) throws IOException, TimeoutException {
   
        //建立连接
        Connection connection = RabbitMQUtils.getConnection();
        // 创建一个信道
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(DlxProducer.EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //绑定死信交换器
        //声明一个队列,并绑定死信交换器
        String queueName = "dlx_queue";
        Map<String, Object> argos = new HashMap<String, Object>();
        argos.put("x-dead-letter-exchange", DlxConsumer.DLX_EXCHANGE_NAME);
        //死信路由键,会替换消息原来的路由键
        //args.put("x-dead-letter-routing-key", "dead");
        channel.queueDeclare(queueName, false, true, false, argos);
        //绑定,将队列和交换器通过路由键进行绑定
        channel.queueBind(queueName, DlxProducer.EXCHANGE_NAME, "#");
        System.out.println("waiting for message........");
        final Consumer consumer = new DefaultConsumer(channel) {
   
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body) throws IOException {
   
                String message = new String(body, "UTF-8");
                //如果是cat的消息确认
                if (envelope.getRoutingKey().equals("cat")) {
   
                    System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
                    channel.basicAck(envelope.getDeliveryTag(), false);
                } else {
   
                    //如果是其他的消息拒绝(queue=false),成为死信消息
                    System.out.println("Will reject[" + envelope
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

汪了个王

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

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

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

打赏作者

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

抵扣说明:

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

余额充值