【RabbitMQ教程】- 实现延时队列

目录

RabbitMQ实现延时队列

Maven依赖

代码实现

1.插件方式


RabbitMQ实现延时队列

RabbitMQ实现延时队列有两种方式:1.死信队列 2.下载插件。

Maven依赖

Maven依赖:

    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

配置:

spring.rabbitmq.host = 127.0.0.1
spring.rabbitmq.port = 5672
spring.rabbitmq.username = guest
spring.rabbitmq.password = guest
spring.rabbitmq.virtualHost = sandbox
# 手动提交
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=3
spring.rabbitmq.listener.simple.max-concurrency=10
spring.rabbitmq.listener.simple.prefetch=1
spring.rabbitmq.cache.channel.size=50

代码实现

1.插件方式

默认已经安装好RabbitMQ的延时插件,接下来我们直接步入主题。

(1)配置类:配置交换机、延时队列、绑定交换机&队列。

package com.atta.msgdactuator.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * author: cms
 * date: 2022/9/27 6:35 PM
 * description: xxx
 */
@Configuration
public class SendMailDelayMonitorConfig {

    public final static String SEND_MAIL_DELAY_EXCHANGE = "edm.send.mail.delay.exchange";

    public final static String SEND_MAIL_DELAY_QUEUE = "edm.send.mail.delay.queue";

    public final static String SEND_MAIL_DELAY_ROUTING_KEY = "edm.send.mail.routing.key";


    @Bean
    public CustomExchange delayMessageExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        /* 自定义交换机*/
        return new CustomExchange(
                SEND_MAIL_DELAY_EXCHANGE, "x-delayed-message",
                true, false, args);
    }

    @Bean
    public Queue delayMessageQueue() {
        return new Queue(SEND_MAIL_DELAY_QUEUE,
                true,
                false,
                false);
    }

    @Bean
    public Binding bindingDelayExchangeAndQueue() {
        return BindingBuilder.bind(delayMessageQueue())
                .to(delayMessageExchange())
                .with(SEND_MAIL_DELAY_ROUTING_KEY)
                .noargs();
    }

}

(2)生产者

package com.atta.msgdactuator.notification;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * author: cms
 * date: 2022/9/27 6:00 PM
 * description: 发送延时消息。
 */
@Slf4j
@Component
public class SendMailDelayMonitorProduce {

    // 此实例是公司组件的rabbitTemplate,如果想自己绑定confirm、return事件,可将rabbitTemplate实例设置为scope。
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 功能描述:发送延时消息。
     * 异常声明:由调用者捕获。
     *
     * @param message           消息内容
     * @param delayMs           延迟时间 单位:毫秒
     * @param correlationData   全局唯一id
     * @param exchange          交换机
     * @param routingKey        消息路由键
     * @return
     */
    public void sendDelayMq(Object message,
                            Integer delayMs,
                            CorrelationData correlationData,
                            String exchange,
                            String routingKey) {
        log.info("[SendMailDelayHandleProduce#sendDelayMq] REQ. start to send mq. msg:[{}]", message);
        if (delayMs == null) {
            delayMs = 0;
        }
        // 发送消息时指定 header 延迟时间
        Integer finalDelayMs = delayMs;
        rabbitTemplate.convertAndSend(
                exchange,
                routingKey,
                message,
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        // 设置消息持久化
                        message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                        message.getMessageProperties().setDelay(finalDelayMs);
                        return message;
                    }
                }, correlationData);
        log.info("[SendMailDelayHandleProduce#sendDelayMq] END. finish to send mq. msg:[{}]", message);
    }
}

(3)消费者

package com.atta.msgdactuator.listen;

import com.atta.infra.common.api.model.Mid;
import com.atta.infra.msgdactuator.api.enums.MessageSendRequestStatus;
import com.atta.infra.utils.AssertUtil;
import com.atta.infra.utils.JsonUtils;
import com.atta.msgdactuator.config.SendMailDelayMonitorConfig;
import com.atta.msgdactuator.dal.dao.MessageSendRequestDAO;
import com.atta.msgdactuator.dal.dataobject.MessageSendRequestDO;
import com.atta.msgdactuator.enums.TaskProcessType;
import com.atta.msgdactuator.model.send.SendMailDelayModel;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;

/**
 * author: cms
 * date: 2022/9/27 6:40 PM
 * description: 监听延时消息,并做告警。
 */
@Component
@Slf4j
public class SendMailDelayMonitorConsumer {

    @Autowired
    private MessageSendRequestDAO messageSendRequestDAO;

    @RabbitListener(queues = SendMailDelayMonitorConfig.SEND_MAIL_DELAY_QUEUE)
    @RabbitHandler
    public void handle(Message msg, Channel channel) {
        String msgID       = "";
        long   deliveryTag = msg.getMessageProperties().getDeliveryTag();
        try {
            /* 获取关联性数据,发送的时候设设置的(使用关联性数据来做msgID)*/
            Object correlationObj = msg.getMessageProperties().getHeaders().get("spring_returned_message_correlation");
            if(correlationObj != null){
                msgID = correlationObj.toString();
            }
            String msgContent =  new String(msg.getBody(), Charset.defaultCharset());
            log.info("[SendMailDelayHandleConsumer#handle] Received msgID:[{}]," +
                    " deliveryTag:[{}], msgContent:[{}]", msgID, deliveryTag, msgContent);
            /* 业务逻辑处理*/
            doHandler(msgContent);
            /* 手动ACK*/
            channel.basicAck(deliveryTag, false);
            log.info("[SendMailDelayHandleConsumer#onDelayMessage] Handler Succeeded msgID:[{}]", msgID);
        } catch (Exception e) {
            log.error("[SendMailDelayHandleConsumer#handler] ERROR msgID:[{}]", msgID, e);
        }
    }

    private void doHandler(String msgContent) {
        AssertUtil.notBlank(msgContent, "msgContent is null or empty");
        SendMailDelayModel sendMailDelayModel = JsonUtils.parseObject(msgContent, SendMailDelayModel.class);
        String type   = sendMailDelayModel.getType();
        String taskId = sendMailDelayModel.getTaskId();
        String firmId = sendMailDelayModel.getFirmId();
        Mid    mid    = sendMailDelayModel.getMid();
        log.info("[SendMailDelayHandleConsumer#doHandler]. taskId:[{}], type:[{}], firmId:[{}], mid:[{}]", taskId, type, firmId, mid);
        MessageSendRequestDO    message = messageSendRequestDAO.selectByTaskId(mid, taskId);
        AssertUtil.notNull(message, "[SendMailDelayHandleConsumer#doHandler]. messageSendRequestDO is null. taskId:[" + taskId,"], mid:[" + mid +"]");
        MessageSendRequestStatus status = message.getStatus();
        log.info("[SendMailDelayHandleConsumer#doHandler]. taskId:[{}], status:[{}], firmId:[{}], mid:[{}]", taskId, status, firmId, message.getMid());
        // 以下日志接入钉钉报警
        if (TaskProcessType.TASK_PROCESS_OF_ENGINE.getType().equals(type) && !MessageSendRequestStatus.COMPLETED.equals(status)){
           log.error("[SendMailDelayHandleConsumer#doHandler]. Task timeout notify. 【引擎端】任务处理超时报警! taskId:[{}], mid:[{}], firmId:[{}], currentStatus:[{}]",
                   taskId, mid, firmId, status);
        }else if(TaskProcessType.TASK_PROCESS_OF_CHANNEL.getType().equals(type) && !MessageSendRequestStatus.FINISHED.equals(status)){
            log.error("[SendMailDelayHandleConsumer#doHandler]. Task timeout notify. 【渠道端】任务处理超时报警! taskId:[{}], mid:[{}], firmId:[{}], currentStatus:[{}]",
                    taskId, mid, firmId, status);
        }else {
            log.info("[SendMailDelayHandleConsumer#doHandler]. Task process finish. taskId:[{}], mid:[{}], firmId:[{}], currentStatus:[{}]",
                    taskId, mid, firmId, status);
        }
    }
}

(4)发送消息

public void sendMailDelayMonitor(MessageSendRequestDO request, TaskProcessType taskProcessType, Integer delayTimeMinute){
        try {
            AssertUtil.notNull(delayTimeMinute, "[MessageSendRequestServiceImpl#sendMailDelayMonitor] ERROR. delayTimeMinute is null.");
            Integer delayTImeMs = delayTimeMinute * 60 * 1000;
            String mqMessageId  = IdWorker.getId() + "";
            SendMailDelayModel message = SendMailDelayModel.builder()
                    .type(taskProcessType.getType())
                    .taskId(request.getTaskId())
                    .firmId(request.getFirmId())
                    .mid(request.getMid()).build();
            String messageJsonStr = JsonUtils.toJSONString(message);
            sendMailDelayMonitorProduce.sendDelayMq(
                    messageJsonStr,
                    delayTImeMs,
                    new CorrelationData(mqMessageId),
                    SendMailDelayMonitorConfig.SEND_MAIL_DELAY_EXCHANGE,
                    SendMailDelayMonitorConfig.SEND_MAIL_DELAY_ROUTING_KEY
            );
        }catch (Exception e) {
            log.error("[SendEmail#send] ERROR. fail to record [channel] task start time. taskId:[{}], mid:[{}], requestId:[{}]",
                    request.getTaskId(), request.getMid(), request.getRequestId(), e);
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@来杯咖啡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值