【RabbitMQ】延迟队列

介绍

延迟队列中的消息,指的是消息发送成功之后,并不想立马被消费,而是等待一段时间后再消费

常见场景

1、手机购物,下单但未支付的订单,在半小时后自动取消订单

2、12306购票,下单但未支付的订单,会取消占座和订单

3、小程序排队取号,15分钟后不确认排队,号就归属于他人... 

实现思路

可以使用rabbitmq_delayed_message_exchange插件来实现

主要思路和死信队列差不多,这个消息是基于插件把消息存放在延时交换机中

1、生产者将消息和路由键发送到指定的延时交换机上

2、延时交换机存放消息,等待消息到期根据路由键找到绑定自己的队列,并把消息发送出去

3、队列再把消息发送给监听它的消费者

四个角色三个过程 

安装插件

1、下载插件

https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases

2、放置到指定目录

把下载好的插件放到rabbitmq-server的安装路径下的plugins目录下

3、启动并加载插件

#查看插件集
rabbitmq-plugins list 
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

4、重启rabbitmq服务

systemctl restart rabbitmq-server

5、验证是否安装成功

进入RabbitMQ控制台,查看Exchange,如图说明插件安装成功

 

代码演示

1、添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、添加配置信息

spring.application.name=springboot_rabbitmq
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.virtual-host=/
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672

3、编写mq延迟配置类

@Configuration
public class RabbitMqDelayedConfig {

    @Bean
    public Queue delayedQueue() {
        Queue queue = new Queue("queue.delayed", false, false, false, null);
        return queue;
    }

    /**
     * 声明延迟交换机
     */
    @Bean
    public Exchange delayedExchange() {
        Map<String, Object> props = new HashMap<>();
        //采用广播模式,做类似闹钟提醒
        props.put("x-delayed-type", ExchangeTypes.FANOUT);
        //声明延迟队列类型的交换机
        Exchange exchange = new CustomExchange("exchange.delayed", "x-delayed-message", true, false, props);
        return exchange;
    }

    @Bean
    public Binding delayedBinding() {
        return BindingBuilder.bind(delayedQueue()).to(delayedExchange()).with("key.delayed").noargs();
    }
}

4、编写延迟队列监听类

@Component
public class DelayedListener {
    @RabbitListener(queues = "queue.delayed")
    public void broadcastAlarm(Message message, Channel channel) throws IOException {
        System.out.println(new String(message.getBody(), "utf-8"));
        System.out.println("================" + new Date() + "====================");
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

5、编写接口发送消息

@RestController
public class DelayedController {
    @Autowired
    private AmqpTemplate amqpTemplate;

    @RequestMapping("/delayed/{seconds}")
    public String toMeeting(@PathVariable Integer seconds) throws UnsupportedEncodingException {
        // RabbitMQ只会检查队列头部的消息是否过期,如果过期就放到死信队列
        // 假如第一个过期时间很长,10s,第二个消息3s,则系统先看第一个消息,等到第一个消息过期,放到DLX死信队列
        // 此时才会检查第二个消息,但实际上此时第二个消息早已经过期了,但是并没 有先于第一个消息放到DLX死信队列。
        // 插件rabbitmq_delayed_message_exchange会帮我们解决这个问题
        MessageProperties properties = new MessageProperties();
        properties.setHeader("x-delay", seconds * 1000);
        Message message = new Message((seconds + "秒后大门口集合!").getBytes("utf-8"), properties);
        // 如果不设置message的properties,也可以使用下述方法设置x-delay属性的值
//        amqpTemplate.convertAndSend("exchange.delayed", "key.delayed", message, msg -> {
//            //使用定制的属性x-delay设置过期时间,也就是提前5s提醒,当消息转换完,设置消息头字段
//            msg.getMessageProperties().setHeader("x-delay", seconds * 1000);
//            return msg;
//        });
        amqpTemplate.convertAndSend("exchange.delayed", "key.delayed", message);
        System.out.println("================" + new Date() + "====================");
        return "已经通知大家了";
    }
}

6、请求接口并验证是否延迟

请求接口:http://localhost:8080/delayed/5

五秒的延迟消费,然后查看一下控制台日志打印

 两次时间打印相差5s,延迟队列生效,集成完毕

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值