rabbitmq死信队列、延迟队列、幂等性保障

文章目录

  • 死信队列
    • 什么是死信
      • 消息成为死信的三种情况
    • 使用死信
      • 在idea中使用配置文件创建
      • 在可视化web创建
    • 延迟队列
    • 消息幂等性保障
      • 实现步骤

死信队列

什么是死信

死信队列,英文缩写DLX。Dead Letter Exchange(死信交换机),当消息成为Dead message(死信信息)后,可以被重新发送到另一个交换机上,这就是DLX
rabbitmq死信队列、延迟队列、幂等性保障_第1张图片

消息成为死信的三种情况

  1. 队列消息长度到达限制;
  2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
  3. 原队列存在消息过期设置,消息到达超时时间未被消费;

使用死信

队列绑定死信交换机:

给队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key
rabbitmq死信队列、延迟队列、幂等性保障_第2张图片

在idea中使用配置文件创建

rabbitmq死信队列、延迟队列、幂等性保障_第3张图片
在配置类中编写代码,确保程序启动配置类中的实例对象可以被加载

@Configuration
public class ExchangeAndQueue {
    private final String QUEUE="queue";//普通队列
    private final String DEAD_QUEUE="dead_queue";//死信队列
    private  final String EXCHANGE="exchange";//普通交换机
    private  final String DEAD_EXCHANGE="dead_exchange";//死信交换机

    //java代码创建普通的队列
    @Bean
    public Queue queue(){
        return QueueBuilder
                .durable(QUEUE)//持久化队列(参数为队列名称)
                .withArgument("x-message-ttl",20000)//设置超时时间
                .withArgument("x-max-length",10)//最大个数
                .withArgument("x-dead-letter-exchange",DEAD_EXCHANGE)//设置死信队列
                .withArgument("x-dead-letter-routing-key","error")//设置死信路由key
                .build();//创建
    }

    //创建死信队列
    @Bean
    public Queue dead_queue() {
        /**
         * durable(DEAD_QUEUE)持久化队列(参数为队列名称)
         * build()创建
         */
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }

    //创建普通交换机
    @Bean
    public Exchange exchange(){
        /**
         * directExchange(EXCHANGE)交换机类型(交换机名称)
         * build()创建
         */
        return ExchangeBuilder.directExchange(EXCHANGE).build();
    }

    //创建死信交换机
    @Bean
    public Exchange dead_exchange(){
        /**
         * directExchange(DEAD_EXCHANGE)交换机类型(交换机名称)
         * build()创建
         */
        return  ExchangeBuilder.directExchange(DEAD_EXCHANGE).build();
    }

    //绑定普通队列和普通1交换机
    @Bean
    public Binding binding(){
        /**
         * bind(queue())要绑定的队列对象
         * to(exchange())绑定到的交换机对象
         * with("error")路由key
         * noargs();没有额外参数
         * @return
         */
        return  BindingBuilder.bind(queue()).to(exchange()).with("error").noargs();
    }

    //绑定死信队列和死信交换机
    @Bean
    public Binding dead_binding(){
        /**
         * bind(dead_queue())要绑定的队列对象
         * to(dead_exchange())绑定到的交换机对象
         * with("error")路由key
         * noargs();没有额外参数
         * @return
         */
        return  BindingBuilder.bind(dead_queue()).to(dead_exchange()).with("error").noargs();
    }
}

在可视化web创建

  1. 创建普通交换机
    rabbitmq死信队列、延迟队列、幂等性保障_第4张图片
  2. 创建死信交换机
    rabbitmq死信队列、延迟队列、幂等性保障_第5张图片
  3. 创建死信队列
    rabbitmq死信队列、延迟队列、幂等性保障_第6张图片
  4. 创建普通队列
    rabbitmq死信队列、延迟队列、幂等性保障_第7张图片
  5. 绑定队列和交换机
    rabbitmq死信队列、延迟队列、幂等性保障_第8张图片
    挨个绑定普通队列、交换机和死信队列、交换机
    rabbitmq死信队列、延迟队列、幂等性保障_第9张图片

延迟队列

rabbitmq死信队列、延迟队列、幂等性保障_第10张图片通过在普通队列中设置失效时间,当普通队列中的信息超出时间未被消费时,就会进入死信队列中,
案例:超出30分钟未支付就删除订单并且让库存回滚

消息幂等性保障

幂等性: 无论执行多少次,得到的结果和第一次都是相同的。 LOL中的英雄大招技能,在冷却好了到放过等待大招cd过程中,只有一次点击有效。这就是幂等性

防止在消费者进行回应时因为一系列问题未响应给队列,从而不删除此信息,导致再次消费,保证了消息不被重复消费。

实现步骤

这里使用redis完成
rabbitmq死信队列、延迟队列、幂等性保障_第11张图片

  1. 开启redis
    rabbitmq死信队列、延迟队列、幂等性保障_第12张图片
  2. 引入依赖
 <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
    dependencies>
  1. 配置application.yml文件
#配置rabbitmq的IP
spring:
  rabbitmq:
    host: 192.168.11.222
    #开启手动确认
    listener:
      simple:
        acknowledge-mode: manual
   #配置redis的端口和IP
  redis:
    host: 192.168.11.222
    port: 6379
server:
  port: 8002
  1. 代码实现
    在配置类中写
@Component
public class MyListener {
	//注入redisTemplate用来操作redis数据库
    @Autowired
    private RedisTemplate redisTemplate;
    //@RabbitListener(queues = "queue")监听rabbitmq的那个队列
    @RabbitListener(queues = "queue")
    /**
    *Message msg 要消费的信息
    *Channel channel 信道对象
    */
    public void listener(Message msg, Channel channel) throws  Exception{
		//先连接redis数据库查看是否有用此信息的唯一标识为key的记录 
        Object o = redisTemplate.opsForValue().get(msg.getMessageProperties().getDeliveryTag());
        //判断是否取到值,取到就说明已经进行过操作,没有就是第一次进行业务代码
        if(o==null){
            //业务代码
            try {
                System.out.println("完成业务功能");
                //完成业务后通过redisTemplate.opsForValue().set方法往redis中存放key为此信息的唯一标识为key的记录 ,方便下次如果在重复操作此信息时进行判断
                redisTemplate.opsForValue().set(msg.getMessageProperties().getDeliveryTag(), "ykq");
                //完成后手动把信息从队列中消费删除掉
                channel.basicAck(msg.getMessageProperties().getDeliveryTag(), true);
            }catch (Exception e){
            //如果出现异常就从队列中删除掉此信息,并且要求生产者重新发送信息到队列中
                channel.basicNack(msg.getMessageProperties().getDeliveryTag(),true,true);
            }
        }else{
        //否则就是重复操作,就直接从队列中删除掉此信息
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(),true);
        }
    }
}

你可能感兴趣的:(rabbitmq,rabbitmq,redis,队列)