RabbitMQ插件-延时交换机,一种替代定时任务的方案

背景

目前很多业务都需要一些延后的处理方案,比如说:订单30分钟后过期自动关闭,发出红包24小时后未领取完毕需要退款等等,需要一定时间后进行一个结算或是其他的操作,就可以用到我记录的这个方案RabbitMQ的延时交换机,非常简单且好用。

准备

怎么安装MQ就不说了网上帖子一大堆
怎么安装延时交换机插件:
安装MQ延时交换机插件

使用场景

首先写一些枚举,因为到处都要用,统一收束起来写成枚举

public class RabbitMQConstant {
//交换机类型
public static final String DELAY_EXCHANGE_NAME = "delay_exchange";
//队列
public static final String RED_PACKET_DELAY_QUEUE_NAME = "red_packet_delay_queue";
//路由
public static final String RED_PACKET_DELAY_QUEUE_ROUTING_KEY = "red_packet_delay_queue_key";
}

然后标记一下配置,告诉系统谁是谁,预备干什么

@Configuration
public class RabbitMQConfig {
   //声名队列
@Bean(RED_PACKET_DELAY_QUEUE_NAME)
Queue redPacketDelayQueue() {return QueueBuilder.durable(RED_PACKET_DELAY_QUEUE_NAME).build();}

//声名交换机
@Bean(DELAY_EXCHANGE_NAME)
CustomExchange delayExchange() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct");
    return new CustomExchange(DELAY_EXCHANGE_NAME, "x-delayed-message", true, false, args);
}

//绑定队列和交换机 指定路由键
@Bean
Binding redPacketDelayQueueBinding(@Qualifier(RED_PACKET_DELAY_QUEUE_NAME) Queue orderDelayQueue, @Qualifier(DELAY_EXCHANGE_NAME) CustomExchange delayExchange) {
    return BindingBuilder.bind(orderDelayQueue).to(delayExchange).with(RED_PACKET_DELAY_QUEUE_ROUTING_KEY).noargs();
}
}

然后就是老生常谈的生产消费者了

生产者 用于提交消息到 MQ中

/**
 *
 * @param sendContent 消息
 * @param messageId 唯一id
 * @param exchangeName 交换机
 * @param key 路由键
 * @param delayTime 延迟时间(秒)
 */
public void publishRedPacket(String sendContent, String messageId, String exchangeName, String key, Integer delayTime) {
    rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
        assert correlationData != null;
        String message_Id = correlationData.getId();
        //返回成功,表示消息被正常投递到交换机
        if (ack) {
            log.info("XXX递到交换机成功,messageId:{}", message_Id);
        } else {
            String mes="交换机不可达,口令为:+"+message_Id+" 原因:"+cause;
            LogMgr.error(log, new BizException(mes));
        }
    });
    // 在实际中ID 应该是全局唯一 能够唯一标识消息 消息不可达的时候触发ConfirmCallback回调方法时可以获取该值,进行对应的错误处理
    CorrelationData correlationData = new CorrelationData(messageId);

    rabbitTemplate.convertAndSend(exchangeName, key, sendContent, message -> {
        // 设置延迟时间
        message.getMessageProperties().setDelay(delayTime*1000);//ms单位
        return message;
    }, correlationData);
}

消费者获取队列中的消息然后消费

//消费者 指定队列获取信息消费
@RabbitListener(queues = RED_PACKET_DELAY_QUEUE_NAME)
@SneakyThrows
public void dealExpireOrder(Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag, String orderInfo) {
    log.debug("收到XXXX信息:" + orderInfo);
    try {	
    //然后进行你的逻辑(例如:订单改变状态,红包退回)
);
    } catch (Exception e) {
        //消息重新入队
        LogMgr.error(log, new BizException(orderInfo + "处理失败" + e.getMessage()));
    } finally {
        channel.basicAck(tag, true);
    }
}

最后贴一个调用方式

// 发消息
try {
	//过期时间 创建一个时间 这个时间可以是XXXS 后需要做什么事儿
    String duration = jsonMap.get("duration").toString(); 
    //然后直接调用生产者 发送消息 再XXXs后进行消费 消费者放你们的逻辑
    mqProducer.publishRedPacket(JSON.toJSONString(redPacketSend), //消息
     String.valueOf(redPacketSend.getCommand()), //唯一ID
      DELAY_EXCHANGE_NAME, //队列
      RED_PACKET_DELAY_QUEUE_ROUTING_KEY, //路由键
      Integer.valueOf(duration)); //过期时间
} catch (Exception e) {
    log.info("投递消息异常:{}", e.getMessage());

}

这只是一个简单的方案,这种方案也可以替代一些定时任务,只需要再消费的时候再次调用生产者投递到队列这样就能一直循环下去。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值