Use rabbitMq achieve timing tasks

Scene: My project is a content management center, the upper and lower frame required to achieve timing function article & advertising.

Implement alternatives are:

  1. Design their own mission timed polling table
  2. redis expired events
  3. rabbitMQ the TTL, by setting the timeout message, switch x-dead-letter-exchange, to be transferred to the consumer after the timeout queue implementation.
  4. There are other additional programs, select here 3

A picture is worth a thousand words, design is as follows:

Paste the code below:

First, the configuration file

RabbitMQ configuration, the definition of the switches, queues, and binding according routingkey. For greater clarity not confusion, specially divided into several configuration files:

RabbitConfigCommon: Configuration 3 switches, RabbitTemplate RabbitListenerContainerFactory, each queue defined & Title routing switch & built. The following advertising / Notice the upper and lower frame, each message is a configuration file: define the appropriate queue, routing key bindings switch, binding policy settings and parameters:

RabbitConfigForAdvOff, RabbitConfigForAdvOn, RabbitConfigForArticleOff, RabbitConfigForArticleOn, RabbitConfigForDeathFinal (final dead letter queue configuration)
 

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);

    }
}

Second, Producer

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;
        });
    }


}

Third, consumers

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;

    }
}

Fourth, the test class

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());
    }
}

V. Other

Task bean:

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

    private Date triggerTime;


}

Other business service strategy ...........

Published 14 original articles · won praise 4 · Views 2737

Guess you like

Origin blog.csdn.net/www_tlj/article/details/103938897