此方法已用到公司商城项目,可用。
业务:双十一商品按指定时间上架;
拼团活动按指定时间上下架;
预售活动按指定时间上下架;
优惠卷活动按指定时间上下架;
思路: 1.定时任务 --可行,但不合适
缺点:因为活动会有很多很多,不知道这些活动的具体的上架时间,也就是说,定时任务只能设置每秒执行一次,服务器的压力大。
2.死信来做延时 --不可行。
死信原理:将消息投放到某队列中(表面队列),改队列无消费者,消息时间到了无法消费,变为死消息,进入死信队列,进行正真的消费,来做到消息的延时。
原因:比如我双十一有两个拼团活动,我先设置拼团A在11.11日0点上架,然后设置拼团B在11.10日晚上10点就开始上架,这里因为A的消息先进入队列,B消息后进入队列,A消息在消费前会阻塞B消息消费的,所以11.10日晚上10点B消息延时的时间到了,B消息消费不了,消息消费被阻塞了,要等A消息消费了,B消息才能消费。
3.rabbitmq的插件做动态延时 --可行
原理:将需要延时的消息投放到延迟交换机里,时间到了,才会通过路由转发到队列里直接消费,避免了消息在队列做延迟可能发生的阻塞情况。
插件下载链接:https://www.rabbitmq.com/community-plugins.html
选择这个 :rabbitmq延迟交换的这个
配置插件:
1.windows
下载后把插件放到 plugins 里面,然后到 sbin里面打开cmd,执行 rabbitmq-plugins enable rabbitmq_delayed_message_exchange 命令,然后重启mq即可
2.linux
下载好后解压到plugins里面,执行命令 rabbitmq-plugins enable rabbitmq_delayed_message_exchange,然后重启mq即可
pmx配置
<!-- MQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
boot项目配置mq
spring:
rabbitmq:
host: ***
port: 5672
username: ***
password: ***
#设置手动ack回执
listener:
simple:
acknowledge-mode: manual
#none 不确认,auto 自动确认 manual 手动确认
配置rabbitmq的队列、交换机及其转发路由
//商品按指定上架时间相关的常量
private final static String EXCHANGE_NAME = "pms_publish_exchange";
private final static String QUEUE_NAME = "pms_publish_queue";
private final static String ROUTE_KEY = "pms_publish_routekey";
/**
* 绑定的交换机,使用rabbitmq_delayed_message_exchange
*/
@Bean("prductExchange")
public CustomExchange productExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(EXCHANGE_NAME,"x-delayed-message",true, false,args);
}
/**
* 消费队列
*/
@Bean("productQueue")
public Queue productQueue() {
return new Queue(QUEUE_NAME);
}
/**
* 延迟队列绑定到交换机
*/
@Bean
public Binding productBinding(@Qualifier("productQueue") Queue assembleQueue, @Qualifier("prductExchange") CustomExchange cfgUserDelayExchange){
return BindingBuilder.bind(assembleQueue).to(cfgUserDelayExchange).with(ROUTE_KEY).noargs();
}
生产者:
@Component
public class RabbitMQPublishSender {
//商品上架
private final static String EXCHANGE_NAME = "pms_publish_exchange";
private final static String QUEUE_NAME = "pms_publish_queue";
private final static String ROUTE_KEY = "pms_publish_routekey";
@Autowired
private AmqpTemplate amqpTemplate;
public void productPublish(Long productId, Long expirationTime) {
this.amqpTemplate.convertAndSend(EXCHANGE_NAME, ROUTE_KEY, productId, message -> {
// 如果配置了 params.put("x-message-ttl", 5 * 1000); 那么这一句也可以省略,具体根据业务需要是声明 Queue 的时候就指定好延迟时间还是在发送自己控制时间
message.getMessageProperties().setHeader("x-delay",expirationTime);
return message;
});
}
}
消费者:
@Component
public class RabbitMQPublishReceiver {
@Autowired
private PmsProductService pmsProductService;
@RabbitListener(queues = "pms_publish_queue")
@RabbitHandler
public void productDelayQueue(Long productId, Message message, Channel channel) throws IOException {
//ack回执
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//商品上架
pmsProductService.publish(productId);
}
}
按指定时间上架业务:
这里业务处理的部分各不相同直接上图好了,主旨就是把延时的时间算出来放入生产者即可。
上架的业务:
就是简单的改个上架字段的状态,直接上图。