RabbitMQ 实现延时队列

  • 应用场景
    用户在商城下单成功并点击去支付后在指定时间未支付时自动失效
    常用的实现方案:
    1.mq延时队列
    2.生成订单时写一条缓存, 监听缓存过期
    3.使用spring + quartz定时任务, 生成订单时写一条定时任务, 执行时如果未支付,则进行处理
    4.将订单过期时间信息写入mysql,轮询查询mysql (性能差, 时效性差!)

  • 利用Mq实现方案
    1.死信队列
    2.利用插件实现延时

  • 死信队列的实现
    注意: 死信队列写在前, 延时队列写在后! 血的教训!!

/**
 - 消息队列相关配置
 -  - @author Lynn
 - @date 2022/10/22 16:46
 */
@Configuration
@EnableRabbit
public class RabbitMqConfig {

    /**
     * 订单取消交换机
     */
    @Bean
    DirectExchange orderDirectExchange() {
        return (DirectExchange) ExchangeBuilder.directExchange(MqEnum.ORDER_CANCEL.getExchange()).build();
    }

    /**
     * 订单取消队列
     */
    @Bean
    Queue orderQueue() {
        return QueueBuilder.durable(MqEnum.ORDER_CANCEL.getQueue()).build();
    }

    /**
     * 订单取消路由
     */
    @Bean
    Binding bindingOrder() {
        return BindingBuilder.bind(orderQueue()).to(orderDirectExchange()).with(MqEnum.ORDER_CANCEL.getRoute());
    }

    /**
     * 订单延迟取消交换机
     */
    @Bean
    DirectExchange orderTtlDirectExchange() {
        return (DirectExchange) ExchangeBuilder.directExchange(MqEnum.ORDER_CANCEL_TTL.getExchange()).build();
    }

    /**
     * 订单延迟队列(死信队列)
     */
    @Bean
    Queue orderTtlQueue() {
        return QueueBuilder.durable(MqEnum.ORDER_CANCEL_TTL.getQueue())
            .deadLetterExchange(MqEnum.ORDER_CANCEL.getExchange())
            .deadLetterRoutingKey(MqEnum.ORDER_CANCEL.getRoute())
            .ttl(15 * 60 * 1000)
            .build();
    }

    /**
     * 订单延迟取消路由
     */
    @Bean
    Binding bindingOrderTtl() {
        return BindingBuilder.bind(orderTtlQueue())
            .to(orderTtlDirectExchange())
            .with(MqEnum.ORDER_CANCEL_TTL.getRoute());
    }
}

队列ttl过期时间设置15分钟, 不设置消费者. 15分钟过后, 消息转发至死信队列.

  • 生产者
找了另一个生产者贴过来供参考
/**
 * @author Lynn
 * @date 2023/4/26 10:03
 */
@Component
public class AudioTransformMqProducer implements MqProducer {

    /**
     * 通知消费语音识别队列
     *
     */
    public void noticeAudioTransform(String contextId) {
        produce(MqEnum.AUDIO_TRANSFORM.getExchange(), MqEnum.AUDIO_TRANSFORM.getRoute(), contextId);
    }
}
  • 消费者
/**
 - 创建工单消费者
 -  - @author Lynn
 - @date 2022/10/22 16:46
 */
@Component
@RabbitListener(queues = "queue.work.order.create")
public class WorkOrderCreateConsumer implements MqConsumer<ReserveOrderVo> {

    @Resource(name = "haHaYangLaoOrderService")
    private OrderService orderService;

    @Override
    public void handleMessage(ReserveOrderVo message) {
        LogManager.getRuntimeLog().info("handleMessage:{}", JSON.toJSONString(message));

        // 更新订单中的工单号
        OrderDto build = OrderDto.builder().orderId(message.getOrderId()).workOrderId(message.getWorkOrderId()).build();
        orderService.updateWorkOrderId(build);
    }
}

用死信队列有一个弊端, 就是ttl时间适合队列绑定在一起的, 如果有不同的超时时间, 那么就需要创建多个队列. 所以此场景可以用插件的方式:
利用rabbit插件实现延时队列

  • 利用插件
/**
 * @date: 2022/02/09
 * @description:
 */
@Configuration
@EnableRabbit
public class DeadLetterConfig {

    @Bean
    public Queue delayQueue() {
        return new Queue(IOT_DELAY_QUEUE, true);
    }

    @Bean
    CustomExchange customExchange() {
        Map<String, Object> args = new HashMap<>(1);
        // 这里使用直连方式的路由,如果想使用不同的路由行为,可以修改,如 topic
        args.put("x-delayed-type", "direct");
        return new CustomExchange(IOT_DELAY_EXCHANGE_NAME, IOT_DELAY_EXCHANGE_TYPE, true, false, args);
    }

    @Bean
    Binding bindingDirect() {
        return BindingBuilder.bind(delayQueue()).to(customExchange()).with(IOT_DELAY_ROUTING_NAME).noargs();
    }
}
  • 生产者
/**
 * @date: 2021/12/6
 * @description:
 */
@Component
public class DeadLetterSender {

    @Autowired
    private AmqpTemplate rabbitTemplate;

    public void send(String message, int delayTime) {
        rabbitTemplate.convertAndSend(IOT_DELAY_EXCHANGE_NAME, IOT_DELAY_ROUTING_NAME, message,
                new DelayMessagePostProcessor(delayTime));
    }
}
  • Processor
/**
 * @date: 2022/6/22
 * @description:
 */
public class DelayMessagePostProcessor implements MessagePostProcessor {
    private Integer delayTime;

    DelayMessagePostProcessor(Integer delayTime) {
        this.delayTime = delayTime;
    }

    @Override
    public Message postProcessMessage(Message message) throws AmqpException {
        message.getMessageProperties().setDelay(delayTime);
        return message;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值