使用rabbitMq实现定时任务

场景:我的项目是一个内容管理中心,需要实现文章&广告的定时上下架功能。

实现可选方案有:

  1. 自己设计任务表进行定时轮询
  2. redis的过期事件
  3. rabbitMQ的TTL,通过设定消息的超时时间,交换机的x-dead-letter-exchange,超时后转移到待消费队列实现。
  4. 还有其他更多的方案,此处选择3

一图胜千言,方案设计图如下:

下面贴代码:

一、配置文件

配置rabbitmq,定义各交换机、队列,并根据routingkey进行绑定。为了更清晰不混乱,特意划分为几个配置文件:

RabbitConfigCommon:配置3个交换机、RabbitTemplate RabbitListenerContainerFactory 、定义各队列&交换机&路由建的名称。下面广告/公告上下架,每种消息一个配置文件:定义相应队列、路由键,绑定交换机,设置绑定策略和参数:

RabbitConfigForAdvOff、RabbitConfigForAdvOn 、RabbitConfigForArticleOff、RabbitConfigForArticleOn、RabbitConfigForDeathFinal(最终死信队列配置)
 

package com.pld.content.manager.config;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author tlj
 * @date 2019/12/31
 */


@Configuration
public class RabbitConfigCommon {


    public static final String TRIGGER_REQ_EXCHANGE = "trigger.req.exchange" + "111";
    public static final String TRIGGER_TIMED_EXCHANGE = "trigger.timed.exchange"+ "111";

    public static final String TRIGGER_DEATH_FINAL_EXCHANGE = "trigger.death.exchange"+ "111";
    public static final String TRIGGER_DEATH_FAILED_ROUTING_KEY = "trigger.death.routingkey"+ "111";

    public static final String TRIGGER_REQ_ROUTING_KEY_ARTICLE_ON = "trigger.req.routingkey.article.on"+ "111";
    public static final String TRIGGER_TIMED_ROUTING_KEY_ARTICLE_ON = "trigger.timed.routingkey.article.on"+ "111";

    public static final String TRIGGER_REQ_ROUTING_KEY_ARTICLE_OFF = "trigger.req.routingkey.article.off"+ "111";
    public static final String TRIGGER_TIMED_ROUTING_KEY_ARTICLE_OFF = "trigger.timed.routingkey.article.off"+ "111";

    public static final String TRIGGER_REQ_ROUTING_KEY_ADV_ON = "trigger.req.routingkey.adv.on"+ "111";
    public static final String TRIGGER_TIMED_ROUTING_KEY_ADV_ON = "trigger.timed.routingkey.adv.on"+ "111";

    public static final String TRIGGER_REQ_ROUTING_KEY_ADV_OFF = "trigger.req.routingkey.adv.off"+ "111";
    public static final String TRIGGER_TIMED_ROUTING_KEY_ADV_OFF = "trigger.timed.routingkey.adv.off"+ "111";



    // 公共: 初始请求的延时交换器:包含内容上架,内容下架,文章上架,文章下架四种routingkey。每个消息的延迟时间由生产者对每条消息进行设定,到达延时时间后,转移到 TRIGGER_TIMED_EXCHANGE交换器
    @Bean
    public DirectExchange triggerReqExchange() {
        return new DirectExchange(TRIGGER_REQ_EXCHANGE);
    }
    // 公共: TRIGGER_TIMED_EXCHANGE交换器,此交换器真正被消费者监听。用于转发已耗时消息
    @Bean
    public DirectExchange triggerTimedExchange() {
        return new DirectExchange(TRIGGER_TIMED_EXCHANGE);
    }
    // 公共: TRIGGER_TIMED_FINAL_EXCHANGE交换器,此交换器真正的死信队列,存储消费不成功被拒绝的消息
    @Bean
    public DirectExchange triggerDeathFinalTopicExchange() {
        return new DirectExchange(TRIGGER_DEATH_FINAL_EXCHANGE);
    }




    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMessageConverter(new Jackson2JsonMessageConverter());
        return template;
    }


    @Bean
    public RabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);             //开启手动 ack
        return factory;
    }
}
----------------------------------------
package com.pld.content.manager.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**广告下架:队列交换机配置
 * @author tlj
 * @date 2019/12/31
 */


@Configuration
@AutoConfigureAfter({RabbitConfigCommon.class})
public class RabbitConfigForAdvOff {
    @Autowired
    private RabbitConfigCommon rabbitConfigCommon;


    /**
     *  声明req queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue advOffReqQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_TIMED_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_OFF);
        return new Queue(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ADV_OFF, true, false, false, params);
    }
    /**
     * req queue 绑定到上游交换机(req exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding bind2PrevQueueForAdvOffReq() {
        return BindingBuilder.bind(advOffReqQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerReqExchange()).with(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ADV_OFF);
    }

    /**
     *  timed queue 绑定到上游交换机(timed exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding triggerTimedAdvOffBinding() {
        //  如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(advOffTimedQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerTimedExchange()).with(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_OFF);
    }

    /**
     *  声明timed queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue advOffTimedQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_DEATH_FINAL_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_DEATH_FAILED_ROUTING_KEY);
        return new Queue(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_OFF, true, false, false, params);
    }




}
--------------------------
package com.pld.content.manager.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**广告上架:队列交换机配置
 * @author tlj
 * @date 2019/12/31
 */


@Configuration
@AutoConfigureAfter({RabbitConfigCommon.class})
public class RabbitConfigForAdvOn {
    @Autowired
    private RabbitConfigCommon rabbitConfigCommon;




    /**
     *  声明req queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue advOnReqQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_TIMED_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_ON);
        return new Queue(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ADV_ON, true, false, false, params);
    }
    /**
     * req queue 绑定到上游交换机(req exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding bind2PrevQueueForAdvOnReq() {
        return BindingBuilder.bind(advOnReqQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerReqExchange()).with(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ADV_ON);
    }

    /**
     *  timed queue 绑定到上游交换机(timed exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding triggerDeathAdvOnBinding() {
        //  如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(advOnTimedQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerTimedExchange()).with(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_ON);
    }
    /**
     *  声明timed queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue advOnTimedQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_DEATH_FINAL_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_DEATH_FAILED_ROUTING_KEY);
        return new Queue(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_ON, true, false, false, params);
    }



}
-------------------------
package com.pld.content.manager.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**文章下架:队列交换机配置
 * @author tlj
 * @date 2019/12/31
 */


@Configuration
@AutoConfigureAfter({RabbitConfigCommon.class})
public class RabbitConfigForArticleOff {
    @Autowired
    private RabbitConfigCommon rabbitConfigCommon;




    /**
     *  声明req queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue articleOffReqQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_TIMED_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_OFF);
        return new Queue(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ARTICLE_OFF, true, false, false, params);
    }
    /**
     * req queue 绑定到上游交换机(req exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding bind2PrevQueueForArticleOffReq() {
        return BindingBuilder.bind(articleOffReqQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerReqExchange()).with(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ARTICLE_OFF);
    }

    /**
     *  timed queue 绑定到上游交换机(timed exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding triggerDeathArticleOffBinding() {
        // 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(articleOffTimedQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerTimedExchange()).with(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_OFF);
    }

    /**
     *  声明timed queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue articleOffTimedQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_DEATH_FINAL_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_DEATH_FAILED_ROUTING_KEY);
        return new Queue(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_OFF, true, false, false, params);
    }




}
---------------------
package com.pld.content.manager.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**文章上架:队列交换机配置
 * @author tlj
 * @date 2019/12/31
 */


@Configuration
@AutoConfigureAfter({RabbitConfigCommon.class})
public class RabbitConfigForArticleOn {
    @Autowired
    private RabbitConfigCommon rabbitConfigCommon;



    /**
     * 声明req queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue articleOnReqQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_TIMED_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_ON);
        return new Queue(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ARTICLE_ON, true, false, false, params);
    }

    /**
     * req queue 绑定到上游交换机(req exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding bind2PrevQueueForArticleOnReq() {
        return BindingBuilder.bind(articleOnReqQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerReqExchange()).with(RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ARTICLE_ON);
    }

    /**
     *  timed queue 绑定到上游交换机(timed exchange)并指定routinkey
     * @return
     */
    @Bean
    public Binding triggerDeathArticleOnBinding() {
        // 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(articleOnTimedQueueAndConfigNextExchange()).to(rabbitConfigCommon.triggerTimedExchange()).with(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_ON);
    }
    /**
     *  声明timed queue 并配置下一流向交换机,并指定routingkey
     * @return
     */
    @Bean
    public Queue articleOnTimedQueueAndConfigNextExchange() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", RabbitConfigCommon.TRIGGER_DEATH_FINAL_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", RabbitConfigCommon.TRIGGER_DEATH_FAILED_ROUTING_KEY);
        return new Queue(RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_ON, true, false, false, params);
    }


}
-----------------------
package com.pld.content.manager.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**最终死信:队列交换机配置
 * @author tlj
 * @date 2019/12/31
 */


@Configuration
@AutoConfigureAfter({RabbitConfigCommon.class})
public class RabbitConfigForDeathFinal {
    private static  final long DEATH_FINAL_QUEUE_TTL_MS_TIME = 30*24*3600*1000;// 暂定存30天

    /**
     *  fielded queue 绑定到上游交换机(death final exchange)并指定routinkey为#
     * @return
     */
    @Bean
    public Binding triggerDeathAdvOnBinding(RabbitConfigCommon rabbitConfigCommon) {
        return BindingBuilder.bind(fieldedQueue()).to(rabbitConfigCommon.triggerDeathFinalTopicExchange()).with(RabbitConfigCommon.TRIGGER_DEATH_FAILED_ROUTING_KEY);
    }
    @Bean
    public Queue fieldedQueue() {
        Map<String, Object> params = new HashMap<>();
        // 最终死信队列的消息存活时间 暂定存30天
        params.put("x-message-ttl", DEATH_FINAL_QUEUE_TTL_MS_TIME);

        return new Queue(RabbitConfigCommon.TRIGGER_DEATH_FAILED_ROUTING_KEY, true, false, false, params);

    }
}

二、生产者

package com.pld.content.manager.controller.mq;

import com.pld.content.manager.config.RabbitConfigCommon;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author tlj
 * @date 2019/12/31
 */
@Service
public class TriggerProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;


    public void sendArticleOnTrigger(long expiration,TriggerTaskInfo taskInfo) {
        rabbitTemplate.convertAndSend(RabbitConfigCommon.TRIGGER_REQ_EXCHANGE, RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ARTICLE_ON,taskInfo, message -> {
            // 设置超时时间  毫秒
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            message.getMessageProperties().setExpiration(String.valueOf(expiration));
            return message;
        });
    }
    public void sendArticleOffTrigger(long expiration,TriggerTaskInfo taskInfo) {
        rabbitTemplate.convertAndSend(RabbitConfigCommon.TRIGGER_REQ_EXCHANGE, RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ARTICLE_OFF,taskInfo, message -> {
            // 设置超时时间  毫秒
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            message.getMessageProperties().setExpiration(String.valueOf(expiration));
            return message;
        });
    }
    public void sendAdvOnTrigger(long expiration,TriggerTaskInfo taskInfo) {
        rabbitTemplate.convertAndSend(RabbitConfigCommon.TRIGGER_REQ_EXCHANGE, RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ADV_ON,taskInfo, message -> {
            // 设置超时时间  毫秒
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            message.getMessageProperties().setExpiration(String.valueOf(expiration));
            return message;
        });
    }


    public void sendAdvOffTrigger(long expiration,TriggerTaskInfo taskInfo) {
        rabbitTemplate.convertAndSend(RabbitConfigCommon.TRIGGER_REQ_EXCHANGE, RabbitConfigCommon.TRIGGER_REQ_ROUTING_KEY_ADV_OFF,taskInfo, message -> {
            // 设置超时时间  毫秒
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            message.getMessageProperties().setExpiration(String.valueOf(expiration));
            return message;
        });
    }


}

三、消费者

package com.pld.content.manager.controller.mq;

import com.alibaba.fastjson.JSON;
import com.pld.content.manager.common.enums.ContentResultCode;
import com.pld.content.manager.common.exceptions.PldContentDetailException;
import com.pld.content.manager.config.RabbitConfigCommon;
import com.pld.content.manager.domain.entity.AdvertisingEntity;
import com.pld.content.manager.domain.entity.UserArticleEntity;
import com.pld.content.manager.domain.repository.AdvertisingRepository;
import com.pld.content.manager.domain.repository.UserArticleRepository;
import com.pld.content.manager.domain.service.backend.AdvertisingService;
import com.pld.content.manager.domain.service.backend.UserArticleService;
import com.pld.content.manager.dto.advertising.req.AdvertisingOffDTO;
import com.pld.content.manager.dto.advertising.req.AdvertisingUpDTO;
import com.pld.content.manager.dto.article.req.ArticleOffDTO;
import com.pld.content.manager.dto.article.req.ArticleUpDTO;
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.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;

/**
 * @author tlj
 * @date 2019/12/31
 */
@Service
@Slf4j
public class TriggerConsumer {
    @Autowired
    private UserArticleService userArticleService;
    @Autowired
    private UserArticleRepository userArticleRepository;
    @Autowired
    private AdvertisingRepository advertisingRepository;
    @Autowired
    private AdvertisingService advertisingService;
    private static final String TASK_CANCEL_LOG = "任务已撤销,不执行任务{}";

    @RabbitListener(queues = RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_ON, containerFactory = "rabbitListenerContainerFactory")
    @RabbitHandler
    public void processTriggerArticleOn(TriggerTaskInfo taskInfo, Channel channel, Message message) throws IOException {
        log.info("消费processTriggerArticleOn:{}",taskInfo);
        try{
            UserArticleEntity userArticleEntity = userArticleRepository.findById(taskInfo.getId()).orElseThrow(()->new PldContentDetailException(ContentResultCode.ARTICLE_NO_EXIST,taskInfo.getId()));
            // 验证trigger时间是否被修改过,如果修改过则撤销任务
            if(!ifDateEqual(taskInfo.getTriggerTime(),userArticleEntity.getTriggerUpTime())){
                log.warn(TASK_CANCEL_LOG, JSON.toJSONString(taskInfo));
                // 任务被撤销:拒绝消息,流入最终死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
                return;
            }
            ArticleUpDTO dto = new ArticleUpDTO();
            BeanUtils.copyProperties(taskInfo,dto);
            // 只让当前的trigger时间生效
            userArticleService.publishArticle(dto);

        }catch (Exception e){
            log.error("{}",e);
            // 处理异常:拒绝消息,流入最终死信队列
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            return;
        }
        // 正常流程:返回ack,删除消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

    }
    @RabbitListener(queues = RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ARTICLE_OFF, containerFactory = "rabbitListenerContainerFactory")
    @RabbitHandler
    public void processTriggerArticleOff(TriggerTaskInfo taskInfo, Channel channel, Message message) throws IOException {
        log.info("消费processTriggerArticleOff:{}",taskInfo);
        try{
            UserArticleEntity userArticleEntity = userArticleRepository.findById(taskInfo.getId()).orElseThrow(()->new PldContentDetailException(ContentResultCode.ARTICLE_NO_EXIST,taskInfo.getId()));
            // 验证trigger时间是否被修改过,如果修改过则撤销任务
            if(!ifDateEqual(taskInfo.getTriggerTime(),userArticleEntity.getTriggerOffTime())){

                log.warn(TASK_CANCEL_LOG, JSON.toJSONString(taskInfo));
                // 任务被撤销:拒绝消息,流入最终死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
                return;
            }
            ArticleOffDTO dto = new ArticleOffDTO();
            BeanUtils.copyProperties(taskInfo,dto);
            // 只让当前的trigger时间生效
            userArticleService.putOffArticle(dto);
        }catch (Exception e){
            log.error("{}",e);
            // 处理异常:拒绝消息,流入最终死信队列
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            return;
        }
        // 正常流程:返回ack,删除消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
    @RabbitListener(queues = RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_ON, containerFactory = "rabbitListenerContainerFactory")
    @RabbitHandler
    public void processTriggerAdvOn(TriggerTaskInfo taskInfo, Channel channel, Message message) throws IOException {
        log.info("消费processTriggerAdvOn:{}",taskInfo);

        try{
            AdvertisingEntity advertisingEntity = advertisingRepository.findById(taskInfo.getId()).orElseThrow(()->new PldContentDetailException(ContentResultCode.ADVERTISING_NOT_EXIST,taskInfo.getId()));
            // 验证trigger时间是否被修改过,如果修改过则撤销任务
            if(!ifDateEqual(taskInfo.getTriggerTime(),advertisingEntity.getTriggerUpTime())){
                log.warn(TASK_CANCEL_LOG, JSON.toJSONString(taskInfo));
                // 任务被撤销:拒绝消息,流入最终死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
                return;
            }
            AdvertisingUpDTO dto = new AdvertisingUpDTO();
            BeanUtils.copyProperties(taskInfo,dto);
            // 只让当前的trigger时间生效
            advertisingService.publishAdvertising(dto);
        }catch (Exception e){
            log.error("{}",e);
            // 处理异常:拒绝消息,流入最终死信队列
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            return;
        }
        // 正常流程:返回ack,删除消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }


    @RabbitListener(queues = RabbitConfigCommon.TRIGGER_TIMED_ROUTING_KEY_ADV_OFF, containerFactory = "rabbitListenerContainerFactory")
    @RabbitHandler
    public void processTriggerAdvOff(TriggerTaskInfo taskInfo, Channel channel, Message message) throws IOException {
        try{
            AdvertisingEntity advertisingEntity = advertisingRepository.findById(taskInfo.getId()).orElseThrow(()->new PldContentDetailException(ContentResultCode.ADVERTISING_NOT_EXIST,taskInfo.getId()));
            // 验证trigger时间是否被修改过,如果修改过则撤销任务
            if(!ifDateEqual(taskInfo.getTriggerTime(),advertisingEntity.getTriggerOffTime())){
                log.warn(TASK_CANCEL_LOG, JSON.toJSONString(taskInfo));
                // 任务被撤销:拒绝消息,流入最终死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
                return;
            }
            AdvertisingOffDTO dto = new AdvertisingOffDTO();
            BeanUtils.copyProperties(taskInfo,dto);
            // 只让当前的trigger时间生效
            advertisingService.putOffAdvertising(dto);

        }catch (Exception e){
            log.error("{}",e);
            // 处理异常:拒绝消息,流入最终死信队列
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            return;
        }
        // 正常流程:返回ack,删除消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
    private  boolean ifDateEqual(Date date1,Date date2){
        if( date1==null ){
            throw new PldContentDetailException(ContentResultCode.CONTENT_NEED_PARAM_EXCEPTION,"triggerTime不能为空");
        }
        if(date2==null){
            return false;
        }
        return date1.compareTo(date2)==0;

    }
}

四、测试类

package com.pld.content.manager.controller.mq;

import com.pld.common.util.MsgResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**mqtest
 * @author tlj
 * @date 2019/9/29
 */
@RestController
//@Api(value = "mqtest", tags = "mqtest")
public class MqTestController {
    @Autowired
    private TriggerProducer triggerProducer;

    public String sendOrder(){
        System.out.println("消息发送时间:"+System.currentTimeMillis());
       /**orderProducer.sendArticleOnTrigger();
        orderProducer.sendArticleOffTrigger();
        orderProducer.sendAdvOnTrigger();*/
        TriggerTaskInfo req = new TriggerTaskInfo();
        req.setId("03d7cb2c75044dfcac0be830aa17dda2");
        req.setUserId("admin");
        long duration = 10000*1L;
        Date triggerTime = new Date(System.currentTimeMillis()+duration);
        req.setTriggerTime(triggerTime);
        triggerProducer.sendAdvOffTrigger(duration,req);
        return "sendOrder";
    }

    @GetMapping(value = "/backend/mqtest")
//    @ApiOperation(value = "mqtest", notes = "mqtest")
    public MsgResult<String> mqtest() {

        return MsgResult.successStatic( sendOrder());
    }
}

五、其他

任务bean:

/**
 * @author tlj
 * @date 2019/12/31
 */
@Data
public class TriggerTaskInfo implements Serializable{
    // 业务表主键
    private String id;
    // 操作人id
    private String userId;

    private Date triggerTime;


}

其他业务service略...........

发布了14 篇原创文章 · 获赞 4 · 访问量 2737

猜你喜欢

转载自blog.csdn.net/www_tlj/article/details/103938897