Rabbitmq死信队列

目录

1、什么是死信队列

2、产生死信队列的原因

3、代码实现---直连交换机

3.1、导入依赖

3.2、配置rabbitmq连接信息

3.3、编写配置类

3.4、编写生产者

3.5、编写消费者

3.6、测试

4、死信队列的架构原理

5、死信队列应用场景


1、什么是死信队列

        RabbitMQ死信队列俗称,备胎队列;消息中间件因为某原因拒收消息后,可以转移到死信队列中存放,死信 队列也可以交换机和路由key等。消费者在消费生产者生产的消息时发生了某些特殊情况(下文会说),导致消息无法被正常消费,存放这些未被消费的消息的队列即为死信队列

2、产生死信队列的原因

        1、消息投递到MQ中存放 消息已经过期  消费者没有及时的获取到我们消息,消息如果存放到mq服务器中过期之后,会转移到备胎死信队列存放。

        2、队列达到最大的长度 (队列容器已经满了。

        3、消费者消费多次消息失败就会转移存放到死信队列中。

Rabbitmq死信队列_第1张图片

3、代码实现---直连交换机

3.1、导入依赖

 
        
            org.springframework.boot
            spring-boot-starter-amqp
        

3.2、配置rabbitmq连接信息

spring:
  rabbitmq:
    ####连接地址
    host: 127.0.0.1
    ####端口号
    port: 5672
    ####账号
    username: guest
    ####密码
    password: guest
    ### 虚拟主机  rabbitmq服务器自己创建
    virtual-host: /rkVirtualHost
    listener:
      simple:
        retry:
          ####开启消费者(程序出现异常的情况下会)进行重试
          enabled: true
          ####最大重试次数  到达最大重试次数会自动进入死信队列,前提是未开启ack手动确认
          max-attempts: 3
          ####重试间隔时间
          initial-interval: 3000
        #ack 自动ack确认:auto   手动ack确认:manual
        acknowledge-mode: auto
server:
  port: 8080

###模拟演示死信队列
rk:
  dlx:
    exchange: rk_dlx_exchange
    queue: rk_order_dlx_queue
    routingKey: dlx
  ###备胎交换机
  order:
    exchange: rk_order_exchange
    queue: rk_order_queue
    routingKey: order

        必须设置为自动ack,到达重试次数后才会自动进入私信队列。 如果使用手动ack,需要参考如下:

RabbitMQ重试机制+死信队列_大锅睿的博客-CSDN博客_mq重试机制

3.3、编写配置类

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

@Component
public class DeadLetterMQConfig {
    /**
     * 订单交换机
     */
    @Value("${rk.order.exchange}")
    private String orderExchange;

    /**
     * 订单队列
     */
    @Value("${rk.order.queue}")
    private String orderQueue;

    /**
     * 订单路由key
     */
    @Value("${rk.order.routingKey}")
    private String orderRoutingKey;
    /**
     * 死信交换机
     */
    @Value("${rk.dlx.exchange}")
    private String dlxExchange;

    /**
     * 死信队列
     */
    @Value("${rk.dlx.queue}")
    private String dlxQueue;
    /**
     * 死信路由
     */
    @Value("${rk.dlx.routingKey}")
    private String dlxRoutingKey;

    /**
     * 声明死信交换机
     * @return DirectExchange
     */
    @Bean
    public DirectExchange dlxExchange() {
        return new DirectExchange(dlxExchange);
    }

    /**
     * 声明死信队列
     * @return Queue
     */
    @Bean
    public Queue dlxQueue() {
        return new Queue(dlxQueue);
    }

    /**
     * 声明订单业务交换机
     * @return DirectExchange
     */
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(orderExchange);
    }

    /**
     * 声明订单队列
     * @return Queue
     */
    @Bean
    public Queue orderQueue() {
        // 订单队列绑定我们的死信交换机
        Map arguments = new HashMap<>(2);
        //死信交换机
        arguments.put("x-dead-letter-exchange", dlxExchange);
        //死信队列
        arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
        return new Queue(orderQueue, true, false, false, arguments);
    }

    /**
     * 绑定死信队列到死信交换机
     * @return Binding
     */
    @Bean
    public Binding binding(Queue dlxQueue,DirectExchange dlxExchange) {
        return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
    }


    /**
     * 绑定订单队列到订单交换机
     * @return Binding
     */
    @Bean
    public Binding orderBinding(Queue orderQueue,DirectExchange orderExchange) {
        return BindingBuilder.bind(orderQueue).to(orderExchange).with(orderRoutingKey);
    }
}

        启动后会自动让rabbitmq创建交换机和队列,如果创建失败,一定要去rabbitmq上先删除已有的队列再启动。 

3.4、编写生产者

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class OrderProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 订单交换机
     */
    @Value("${rk.order.exchange}")
    private String orderExchange;
    /**
     * 订单路由key
     */
    @Value("${rk.order.routingKey}")
    private String orderRoutingKey;

    @RequestMapping("/sendOrder")
    public String sendOrder() {
        String msg = "rk 学 rabbitmq";
        //发送消息  参数一:交换机 参数二:路由键(用来指定发送到哪个队列)
        rabbitTemplate.convertAndSend(orderExchange, orderRoutingKey, msg, message -> {
            // 设置消息过期时间 10秒过期    如果过期时间内还没有被消费 就会发送给死信队列
            message.getMessageProperties().setExpiration("10000");
            return message;
        });
        return "success";
    }

}

3.5、编写消费者

订单消费者:

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 订单消费者
 */
@Component
@Slf4j
public class OrderConsumer {

    /**
     * 监听队列回调的方法
     *
     * @param msg
     */
    @RabbitListener(queues = "rk_order_queue")
    public void orderConsumer(String msg) {
        log.info(">>正常订单消费者消息MSG:{}<<", msg);
    }
}

死信消费者:

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


@Slf4j
@Component
public class OrderDlxConsumer {

    /**
     * 死信队列监听队列回调的方法
     *
     * @param msg
     */
    @RabbitListener(queues = "rk_order_dlx_queue")
    public void orderConsumer(String msg) {
        log.info(">死信队列消费订单消息:msg{}<<", msg);
    }
}

3.6、测试

        项目启动后,spring创建交换机和队列的bean以及绑定关系。rabbitmq服务器会自动创建交换机和队列。访问生产者接口

Rabbitmq死信队列_第2张图片

Rabbitmq死信队列_第3张图片

        因为订单消费者正常,并且能够成功消费,所以消息没有进入死信队列。现在我们注释掉订单消费者,这样10秒内消息无法正常消费,就会进入死信交换机再投递给死信队列,由死信队列消费者进行消费。同样访问接口:

Rabbitmq死信队列_第4张图片

Rabbitmq死信队列_第5张图片

        假如有这样一个需要延迟消费的场景,我们可以死信队列来实现延迟队列。

4、死信队列的架构原理

死信队列和普通队列区别不是很大

普通与死信队列都有自己独立的交换机和路由key、队列和消费者。

区别:

        1、生产者投递消息先投递到我们普通交换机中,普通交换机在将该消息投到普通队列中缓存起来,普通队列对应有自己独立普通消费者。

        2、如果生产者投递消息到普通队列中,普通队列发现该消息一直没有被消费者消费的情况下,在这时候会将该消息转移到死信(备胎)交换机中,死信(备胎)交换机对应有自己独立的 死信(备胎)队列 对应独立死信(备胎)消费者。

5、死信队列应用场景

30分钟订单超时设计:网购时下了订单30内未付款,订单将会自动取消,进行回滚加 还原库存。

方案一Redis过期key :

        在用户下单的时候,会使用redis存入一个key,过期时间为30分钟。当这个key过期之后会发送一个事件通知给客户端。这个时候根据订单的id去查询该订单是否付款,如果没有付款,就进行回滚操作(将库存还原)

方案二死信延迟队列实现

        采用死信队列,创建一个普通队列没有对应的消费者 消费消息,在30分钟过后就会将该消息转移到死信备胎消费者实现消费。备胎死信消费者会根据该订单号码查询是否已经支付过,如果没有支付的情况下则会开始回滚库存操作(进行加库存)

你可能感兴趣的:(中间件,java-rabbitmq,rabbitmq,java)