前言

人间清醒

业务场景

用戶在购买商品的时候通常会预购然后没付款,没付款的订单通常会被设置一个自动超时时间如30分钟后超时,所以我们要在订单到30分钟后自动将超时的订单取消。

JUC(DelayQueue)方案

DelayQueue简介
  • DelayQueue是java并发包下的延时阻塞队列,常用于实现定时任务。
  • DelayQueue是一个支持延时获取元素的无界阻塞队列。里面的元素全部都是“可延期”的元素,列头的元素是最先“到期”的元素,

    如果队列里面没有元素到期,是不能从列头获取元素的,哪怕有元素也不行。也就是说只有在延迟期到时才能够从队列中取元素。
  • DelayQueue主要用于两个方面:- 缓存:清掉缓存中超时的缓存数据- 任务超时处理
  • DelayQueue实现了BlockingQueue,所以它是一个阻塞队列。
  • DelayQueue还组合了一个叫做Delayed的接口,DelayQueue中存储的所有元素必须实现Delayed接口。
JUC DelayQueue实现订单超时案例代码 案例代码

定义订单超时对象:

/**
* juc 定义延迟对象信息
* @author wuwentao
*/
@Data
public class Order implements Delayed {
public Order(String orderId,long second){
this.orderId = orderId;
second = second * 1000;
this.timeout = System.currentTimeMillis() + second;
}
private String orderId; // 订单号
private long timeout; // 具体的超时时间 /**
* 延迟任务会自动调用该方法如果是负数则说明对象到了时间
*/
@Override
public long getDelay(TimeUnit unit) {
return this.timeout - System.currentTimeMillis();
} /**
* 定义排序规则
*/
@Override
public int compareTo(Delayed o) {
return (int) (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
}

定义DelayQueue端作为消费者:

/**
* 延迟队列消费者
*/
@Slf4j
@Component
public class JavaDelayQueueConsumer {
// 订单超时对象存储的定时队列
private final DelayQueue<OrderTimeoutDelayed> delayQueue = new DelayQueue<>();
// 为true的时候启动线程,全局只启动一次
private final AtomicBoolean start = new AtomicBoolean(false);
// 任务处理线程
private Thread thread;
/**
* 将需要自动过期的订单放到队列
* @param orderTimeoutDelayed
*/
public void monitor(OrderTimeoutDelayed orderTimeoutDelayed){
delayQueue.add(orderTimeoutDelayed);
} /**
* 启动过期订单处理
*/
@PostConstruct
public void start(){
if(!start.getAndSet(true)){
this.thread = new Thread(()->{
while (true) {
try {
// 获取已超时的订单
OrderTimeoutDelayed take = delayQueue.take();
if(take != null){
log.info("JUC延迟队列订单号:[{}]已超时当前时间为:[{}]",take.getOrderId(), DateUtil.getCuurentDateStr());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
this.thread.start();
log.info("已启动JUC延迟队列消费!");
}
}
}

定义入口请求用于向队列中添加需要自动超时的订单信息:

/**
* JUC实现延迟队列
* @author wuwentao
*/
@Slf4j
@RestController
@RequestMapping("java/dealy/queue")
@AllArgsConstructor
public class JavaDealyQueueProducer {
private JavaDelayQueueConsumer javaDelayQueueConsumer;
private final int defaultTimoutSecond = 10; // 过期秒数
/**
* 发送消息到JUC延迟队列中
* @param message 消息内容
*/
@GetMapping("/sendMessage")
public String sendMessage(@RequestParam(value = "message") String message){
// 监听订单10秒后过期
javaDelayQueueConsumer.monitor(new OrderTimeoutDelayed(message,defaultTimoutSecond));
log.info("当前时间为:[{}] 订单号为:[{}] 超时秒数:[{}]", DateUtil.getCuurentDateStr(),message,defaultTimoutSecond);
return "OK";
}
}

测试生成消息访问接口地址(每一秒访问次一个生成5个需要过期的订单):

http://localhost:8022/java/dealy/queue/sendMessage?message=100000001
http://localhost:8022/java/dealy/queue/sendMessage?message=100000002
http://localhost:8022/java/dealy/queue/sendMessage?message=100000003
http://localhost:8022/java/dealy/queue/sendMessage?message=100000004
http://localhost:8022/java/dealy/queue/sendMessage?message=100000005

控制台打印消费信息:

2022-08-24 15:55:14.435  INFO 18876 --- [nio-8022-exec-1] c.g.b.d.c.JavaDealyQueueProducer         : 当前时间为:[2022-08-24 15:55:14] 订单号为:[100000001] 超时秒数:[10]
2022-08-24 15:55:16.184 INFO 18876 --- [nio-8022-exec-2] c.g.b.d.c.JavaDealyQueueProducer : 当前时间为:[2022-08-24 15:55:16] 订单号为:[100000002] 超时秒数:[10]
2022-08-24 15:55:17.626 INFO 18876 --- [nio-8022-exec-3] c.g.b.d.c.JavaDealyQueueProducer : 当前时间为:[2022-08-24 15:55:17] 订单号为:[100000003] 超时秒数:[10]
2022-08-24 15:55:19.165 INFO 18876 --- [nio-8022-exec-4] c.g.b.d.c.JavaDealyQueueProducer : 当前时间为:[2022-08-24 15:55:19] 订单号为:[100000004] 超时秒数:[10]
2022-08-24 15:55:20.811 INFO 18876 --- [nio-8022-exec-5] c.g.b.d.c.JavaDealyQueueProducer : 当前时间为:[2022-08-24 15:55:20] 订单号为:[100000005] 超时秒数:[10]
2022-08-24 15:55:24.434 INFO 18876 --- [ Thread-8] c.g.b.d.java.JavaDelayQueueConsumer : JUC延迟队列订单号:[100000001]已超时当前时间为:[2022-08-24 15:55:24]
2022-08-24 15:55:26.184 INFO 18876 --- [ Thread-8] c.g.b.d.java.JavaDelayQueueConsumer : JUC延迟队列订单号:[100000002]已超时当前时间为:[2022-08-24 15:55:26]
2022-08-24 15:55:27.625 INFO 18876 --- [ Thread-8] c.g.b.d.java.JavaDelayQueueConsumer : JUC延迟队列订单号:[100000003]已超时当前时间为:[2022-08-24 15:55:27]
2022-08-24 15:55:29.164 INFO 18876 --- [ Thread-8] c.g.b.d.java.JavaDelayQueueConsumer : JUC延迟队列订单号:[100000004]已超时当前时间为:[2022-08-24 15:55:29]
2022-08-24 15:55:30.810 INFO 18876 --- [ Thread-8] c.g.b.d.java.JavaDelayQueueConsumer : JUC延迟队列订单号:[100000005]已超时当前时间为:[2022-08-24 15:55:30]

Redis Key过期事件方案

简介

这里主要使用Redis Key过期事件来实现订单超时案例

Rabbit Key过期时间实现订单超时案例代码

Redis使用的时候将redis配置文件中的该属性从""修改为"Ex"

notify-keyspace-events "Ex"

定义Redis序列化与Key过期监听容器:

/**
* Redis SpringBoot 配置
* @author wuwentao
*/
@Configuration
@AllArgsConstructor
public class RedisConfiguration {
private RedisConnectionFactory redisConnectionFactory; /**
* 模板方法序列化防止乱码
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
} /**
* 配置Redis消息监听容器
* @return {@link:org.springframework.data.redis.listener.RedisMessageListenerContainer}
*/
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer() {
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
return redisMessageListenerContainer;
}
}

定义Key过期监听处理:

/**
* Redis过期Key监听
*/
@Slf4j
@Component
public class RedisKeyExpiredListener extends KeyExpirationEventMessageListener {
public RedisKeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
String key = message.toString();
if(key.startsWith(RedisKeyExpiredController.ORDER_TIMEOUT_PREFIX)){
String orderId = key.replace(RedisKeyExpiredController.ORDER_TIMEOUT_PREFIX,"");
log.info("过期时间:[{}] Redis-key:[{}] 订单号:[{}]", DateUtil.getCuurentDateStr(),key,orderId);
}
}
}

定义入口请求用于向Redis中保存需要过期的订单Key:

@Slf4j
@RestController
@RequestMapping("redis/expired/key")
@AllArgsConstructor
public class RedisKeyExpiredController {
private RedisTemplate redisTemplate;
// Redis需要需要处理的Key前缀
public static final String ORDER_TIMEOUT_PREFIX = "ORDERTIMEOUT-";
// 过期秒数
private final int defaultTimoutSecond = 10; /**
* Redis保存订单号与过期事件
* @param orderId 订单编号
*/
@GetMapping("/send")
public String send(@RequestParam(value = "orderId") String orderId){
ValueOperations valueOperations = redisTemplate.opsForValue();
String key = ORDER_TIMEOUT_PREFIX + orderId;
String value = orderId;
long timeout = defaultTimoutSecond;
TimeUnit seconds = TimeUnit.SECONDS;
valueOperations.set(key,value,timeout, seconds);
log.info("当前时间:[{}] 订单编号:[{}] Redis-Key:[{}] 超时秒数:[{}] ", DateUtil.getCuurentDateStr(),value,key,timeout);
return "OK";
}
}

测试生成消息访问接口地址(每一秒访问次一个生成5个需要过期的订单):

http://localhost:8022/redis/expired/key/send?orderId=100000001
http://localhost:8022/redis/expired/key/send?orderId=100000002
http://localhost:8022/redis/expired/key/send?orderId=100000003
http://localhost:8022/redis/expired/key/send?orderId=100000004
http://localhost:8022/redis/expired/key/send?orderId=100000005

控制台打印消费信息:

2022-08-24 16:26:49.626  INFO 20028 --- [nio-8022-exec-1] c.g.b.d.c.RedisKeyExpiredController      : 当前时间:[2022-08-24 16:26:49] 订单编号:[100000001] Redis-Key:[ORDERTIMEOUT-100000001] 超时秒数:[10]
2022-08-24 16:26:53.124 INFO 20028 --- [nio-8022-exec-4] c.g.b.d.c.RedisKeyExpiredController : 当前时间:[2022-08-24 16:26:53] 订单编号:[100000002] Redis-Key:[ORDERTIMEOUT-100000002] 超时秒数:[10]
2022-08-24 16:26:55.468 INFO 20028 --- [nio-8022-exec-5] c.g.b.d.c.RedisKeyExpiredController : 当前时间:[2022-08-24 16:26:55] 订单编号:[100000003] Redis-Key:[ORDERTIMEOUT-100000003] 超时秒数:[10]
2022-08-24 16:26:57.717 INFO 20028 --- [nio-8022-exec-6] c.g.b.d.c.RedisKeyExpiredController : 当前时间:[2022-08-24 16:26:57] 订单编号:[100000004] Redis-Key:[ORDERTIMEOUT-100000004] 超时秒数:[10]
2022-08-24 16:26:59.703 INFO 20028 --- [nio-8022-exec-7] c.g.b.d.c.RedisKeyExpiredController : 当前时间:[2022-08-24 16:26:59] 订单编号:[100000005] Redis-Key:[ORDERTIMEOUT-100000005] 超时秒数:[10]
2022-08-24 16:26:59.885 INFO 20028 --- [enerContainer-2] c.g.b.d.redis.RedisKeyExpiredListener : 过期时间:[2022-08-24 16:26:59] Redis-key:[ORDERTIMEOUT-100000001] 订单号:[100000001]
2022-08-24 16:27:03.210 INFO 20028 --- [enerContainer-3] c.g.b.d.redis.RedisKeyExpiredListener : 过期时间:[2022-08-24 16:27:03] Redis-key:[ORDERTIMEOUT-100000002] 订单号:[100000002]
2022-08-24 16:27:05.537 INFO 20028 --- [enerContainer-4] c.g.b.d.redis.RedisKeyExpiredListener : 过期时间:[2022-08-24 16:27:05] Redis-key:[ORDERTIMEOUT-100000003] 订单号:[100000003]
2022-08-24 16:27:07.771 INFO 20028 --- [enerContainer-5] c.g.b.d.redis.RedisKeyExpiredListener : 过期时间:[2022-08-24 16:27:07] Redis-key:[ORDERTIMEOUT-100000004] 订单号:[100000004]
2022-08-24 16:27:09.780 INFO 20028 --- [enerContainer-6] c.g.b.d.redis.RedisKeyExpiredListener : 过期时间:[2022-08-24 16:27:09] Redis-key:[ORDERTIMEOUT-100000005] 订单号:[100000005]

JUC与Redis的不足

  1. JUC是存内存操作一旦系统宕机数据将全部丢失。
  2. JUC因为是纯内存操作所以不支持集群。
  3. Redis Key过期前程序突然宕机将造成数据丢失。
  4. 应用程序在集群环境是多个程序都能够监听到这个过期的Key,如果处理不好可能导致重复消费。
  5. Redis缓存所有的Key都会被监听需要自己处理Key去匹配不够灵活

为什么使用RabbitMQ来实现?

单独看这点就能够解决前者的不足: 可靠性:支持持久化,传输确认,发布确认等保证了MQ的可靠性。

更多特性请参考: https://www.cnblogs.com/SimpleWu/p/16618662.html

RabbitMQ死信队列方案

死信队列实现订单超时案例代码

DLX(dead-letter-exchange),死信队列也是一般的队列,当消息变成死信时,消息会投递到死信队列中,经过死信队列进行消费的一种形式,对应的交换机叫死信交换机DLX。

死信队列配置信息定义:

public interface DeadLetterQueueConfig {
// 订单超时处理队列
public static final String ORDER_TIMEOUT_QUEUE = "order.timeout.queue";
// 订单超时处理队列交换机
public static final String ORDER_TIMEOUT_EXCHANGE = "order.timeout.exchange";
// 订单超时处理RoutingKey
public static final String ORDER_TIMEOUT_ROUTING_KEY = "order.timeout.routing.key";
// 死信队列
public static final String DEAD_LETTER_QUEUE = "dead.letter.queue";
// 死信队列交换机
public static final String DEAD_LETTER_EXCHANGE = "dead.letter.exchange";
// 死信队列RoutingKey
public static final String DEAD_LETTER_ROUTING_KEY = "dead.letter.routing.key";
// 死信队列超时时间(10秒)
public static final String X_MESSAGE_TTL ="10000";
}

消费者定义,死信队列消费者定义与订单超时消费队列定义:

@Slf4j
@Component
public class DeadLetterConsumerAnnotatedEdition { /**
* 该队列的消息为死信始终消费不会成功等到到了超时时间则会将消息投递到x-dead-letter-exchange交换机中由绑定的队列来进行处理
*/
@RabbitListener(bindings = {
@QueueBinding(value = @Queue(value = DeadLetterQueueConfig.DEAD_LETTER_QUEUE, arguments =
{@Argument(name = "x-dead-letter-exchange", value = DeadLetterQueueConfig.ORDER_TIMEOUT_EXCHANGE),
@Argument(name = "x-dead-letter-routing-key", value = DeadLetterQueueConfig.ORDER_TIMEOUT_ROUTING_KEY),
@Argument(name = "x-message-ttl", value = DeadLetterQueueConfig.X_MESSAGE_TTL, type = "java.lang.Long")
// ,@Argument(name = "x-max-length",value = "5",type = "java.lang.Integer")队列最大长度
}),//可以指定多种属性
exchange = @Exchange(value = DeadLetterQueueConfig.DEAD_LETTER_EXCHANGE),
key = {DeadLetterQueueConfig.DEAD_LETTER_ROUTING_KEY}
)
})
@RabbitHandler
public void deadLetterConsumer(Message message, Channel channel) throws Exception {
/*
* deliveryTag:该消息的index
* multiple: ture确认本条消息以及之前没有确认的消息(批量),false仅确认本条消息
* requeue: true该条消息重新返回MQ queue,MQ broker将会重新发送该条消息
*/
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} /**
* 处理死信队列超时的订单
*/
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = DeadLetterQueueConfig.ORDER_TIMEOUT_EXCHANGE, durable = "true", type = "direct"),
value = @Queue(value = DeadLetterQueueConfig.ORDER_TIMEOUT_QUEUE, durable = "true"),
key = DeadLetterQueueConfig.ORDER_TIMEOUT_ROUTING_KEY
))
public void canleOrder(String context, Message message, Channel channel) throws IOException {
log.info("当前时间:{} 订单取消订单号:{}", DateUtil.getCuurentDateStr(),context);
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);//仅确认本条消息
} }

定义入口请求用于向死信队列投递消息:

@Slf4j
@RestController
@RequestMapping("rabbit/deadletter/queue")
@AllArgsConstructor
public class RabbitDeadLetterQueueProducer {
private RabbitTemplate rabbitTemplate;
/**
* 投递订单到死信队列中
* @param orderId 订单ID
*/
@GetMapping("/sendMessage")
public String sendMessage(@RequestParam(value = "orderId") String orderId){
rabbitTemplate.convertAndSend(DeadLetterQueueConfig.DEAD_LETTER_EXCHANGE,DeadLetterQueueConfig.DEAD_LETTER_ROUTING_KEY,orderId);
log.info("当前时间:{} 订单号:{}", DateUtil.getCuurentDateStr(),orderId);
return "OK";
}
}

测试生成消息访问接口地址(每一秒访问次一个生成5个需要过期的订单):

http://localhost:8022/rabbit/deadletter/queue/sendMessage?orderId=100000001
http://localhost:8022/rabbit/deadletter/queue/sendMessage?orderId=100000002
http://localhost:8022/rabbit/deadletter/queue/sendMessage?orderId=100000003
http://localhost:8022/rabbit/deadletter/queue/sendMessage?orderId=100000004
http://localhost:8022/rabbit/deadletter/queue/sendMessage?orderId=100000005

控制台打印消费信息:

2022-08-25 16:41:25.150  INFO 19956 --- [nio-8022-exec-9] c.g.b.d.c.RabbitDeadLetterQueueProducer  : 当前时间:2022-08-25 16:41:25 订单号:100000001
2022-08-25 16:41:26.824 INFO 19956 --- [io-8022-exec-10] c.g.b.d.c.RabbitDeadLetterQueueProducer : 当前时间:2022-08-25 16:41:26 订单号:100000002
2022-08-25 16:41:28.689 INFO 19956 --- [nio-8022-exec-1] c.g.b.d.c.RabbitDeadLetterQueueProducer : 当前时间:2022-08-25 16:41:28 订单号:100000003
2022-08-25 16:41:30.453 INFO 19956 --- [nio-8022-exec-2] c.g.b.d.c.RabbitDeadLetterQueueProducer : 当前时间:2022-08-25 16:41:30 订单号:100000004
2022-08-25 16:41:33.256 INFO 19956 --- [nio-8022-exec-3] c.g.b.d.c.RabbitDeadLetterQueueProducer : 当前时间:2022-08-25 16:41:33 订单号:100000005
2022-08-25 16:41:35.153 INFO 19956 --- [ntContainer#1-5] r.t.c.DeadLetterConsumerAnnotatedEdition : 当前时间:2022-08-25 16:41:35 订单取消订单号:100000001
2022-08-25 16:41:36.825 INFO 19956 --- [ntContainer#1-4] r.t.c.DeadLetterConsumerAnnotatedEdition : 当前时间:2022-08-25 16:41:36 订单取消订单号:100000002
2022-08-25 16:41:38.699 INFO 19956 --- [ntContainer#1-6] r.t.c.DeadLetterConsumerAnnotatedEdition : 当前时间:2022-08-25 16:41:38 订单取消订单号:100000003
2022-08-25 16:41:40.458 INFO 19956 --- [ntContainer#1-7] r.t.c.DeadLetterConsumerAnnotatedEdition : 当前时间:2022-08-25 16:41:40 订单取消订单号:100000004
2022-08-25 16:41:43.267 INFO 19956 --- [ntContainer#1-3] r.t.c.DeadLetterConsumerAnnotatedEdition : 当前时间:2022-08-25 16:41:43 订单取消订单号:100000005

延迟消息插件方案

延迟消息插件安装

我这里安装的RabbitMQ版本为3.8.8,这里我在发行版本中下载版本为: rabbitmq-delayed-message-exchange v3.8.x

github地址: https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
同版本地址: https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v3.8.0/rabbitmq_delayed_message_exchange-3.8.0.ez

将下载好的插件(rabbitmq_delayed_message_exchange-3.8.0.ez)复制到plugins目录下(C:\Program Files\RabbitMQ Server\rabbitmq_server-3.8.8\plugins);

进入到sbin目录(C:\Program Files\RabbitMQ Server\rabbitmq_server-3.8.8\sbin)打开cmd窗口执行命令开启插件:

C:\Program Files\RabbitMQ Server\rabbitmq_server-3.8.8\sbin>rabbitmq-plugins enable rabbitmq_delayed_message_exchange
Enabling plugins on node rabbit@LX-P1DMPLUV:
rabbitmq_delayed_message_exchange
The following plugins have been configured:
rabbitmq_delayed_message_exchange
rabbitmq_management
rabbitmq_management_agent
rabbitmq_web_dispatch
Applying plugin configuration to rabbit@LX-P1DMPLUV...
The following plugins have been enabled:
rabbitmq_delayed_message_exchange started 1 plugins.

继续输入命令重启RabbitMQ:rabbitmq-service restart

延迟消息实现订单超时案例代码

延迟消息配置信息定义:

public class DelayedQueueConfig {
// 延迟队列
public static final String DELAYED_QUEUE = "delayed.queue";
// 延迟队列交换机
public static final String DELAYED_EXCHANGE = "delayed.exchange";
// 延迟队列路由KEY
public static final String DELAYED_ROUTING_KEY = "delayed.routing.key";
// 订单过期时间
public static final String ORDER_OUTIME ="10000"; }

消费者定义,延迟消息订单超时消费队列定义:

@Slf4j
@Component
public class DelayedConsumerAnnotatedEdition {
/**
* 延迟队列交换机类型必须为:x-delayed-message
* x-delayed-type 必须设置否则将会报错
*/
@RabbitListener(bindings = {
@QueueBinding(value = @Queue(value = DelayedQueueConfig.DELAYED_QUEUE),
exchange = @Exchange(value = DelayedQueueConfig.DELAYED_EXCHANGE,type = "x-delayed-message",
arguments = {@Argument(name = "x-delayed-type", value = ExchangeTypes.DIRECT)}),
key = {DelayedQueueConfig.DELAYED_ROUTING_KEY}
)
})
@RabbitHandler
public void delayedConsumer(String context, Message message, Channel channel) throws Exception {
log.info("当前时间:{} 订单取消订单号:{}", DateUtil.getCuurentDateStr(),context);
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);//仅确认本条消息
}
}

定义入口请求用于向延迟消息队列投递消息:

@Slf4j
@RestController
@RequestMapping("rabbit/dealyed/queue")
@AllArgsConstructor
public class RabbitDelayedQueueProducer {
private RabbitTemplate rabbitTemplate;
/**
* 投递订单到延迟消息队列中
* @param orderId 订单ID
*/
@GetMapping("/sendMessage")
public String sendMessage(@RequestParam(value = "orderId") String orderId){
rabbitTemplate.convertAndSend(DelayedQueueConfig.DELAYED_EXCHANGE, DelayedQueueConfig.DELAYED_ROUTING_KEY, orderId, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
// 设置消息超时时间
message.getMessageProperties().setHeader("x-delay", DelayedQueueConfig.ORDER_OUTIME);
return message;
}
});
log.info("当前时间:{} 订单号:{}", DateUtil.getCuurentDateStr(),orderId);
return "OK";
}
}

测试生成消息访问接口地址(每一秒访问次一个生成5个需要过期的订单):

http://localhost:8022/rabbit/dealyed/queue/sendMessage?orderId=100000001
http://localhost:8022/rabbit/dealyed/queue/sendMessage?orderId=100000002
http://localhost:8022/rabbit/dealyed/queue/sendMessage?orderId=100000003
http://localhost:8022/rabbit/dealyed/queue/sendMessage?orderId=100000004
http://localhost:8022/rabbit/dealyed/queue/sendMessage?orderId=100000005

控制台打印消费信息:

2022-08-25 17:24:36.324  INFO 17212 --- [nio-8022-exec-1] c.g.b.d.c.RabbitDelayedQueueProducer     : 当前时间:2022-08-25 17:24:36 订单号:100000001
2022-08-25 17:24:38.011 INFO 17212 --- [nio-8022-exec-2] c.g.b.d.c.RabbitDelayedQueueProducer : 当前时间:2022-08-25 17:24:38 订单号:100000002
2022-08-25 17:24:39.606 INFO 17212 --- [nio-8022-exec-3] c.g.b.d.c.RabbitDelayedQueueProducer : 当前时间:2022-08-25 17:24:39 订单号:100000003
2022-08-25 17:24:41.109 INFO 17212 --- [nio-8022-exec-4] c.g.b.d.c.RabbitDelayedQueueProducer : 当前时间:2022-08-25 17:24:41 订单号:100000004
2022-08-25 17:24:42.547 INFO 17212 --- [nio-8022-exec-5] c.g.b.d.c.RabbitDelayedQueueProducer : 当前时间:2022-08-25 17:24:42 订单号:100000005
2022-08-25 17:24:46.395 INFO 17212 --- [ntContainer#2-1] .d.r.d.c.DelayedConsumerAnnotatedEdition : 当前时间:2022-08-25 17:24:46 订单取消订单号:100000001
2022-08-25 17:24:48.037 INFO 17212 --- [tContainer#2-10] .d.r.d.c.DelayedConsumerAnnotatedEdition : 当前时间:2022-08-25 17:24:48 订单取消订单号:100000002
2022-08-25 17:24:49.626 INFO 17212 --- [ntContainer#2-9] .d.r.d.c.DelayedConsumerAnnotatedEdition : 当前时间:2022-08-25 17:24:49 订单取消订单号:100000003
2022-08-25 17:24:51.127 INFO 17212 --- [ntContainer#2-7] .d.r.d.c.DelayedConsumerAnnotatedEdition : 当前时间:2022-08-25 17:24:51 订单取消订单号:100000004
2022-08-25 17:24:52.549 INFO 17212 --- [ntContainer#2-8] .d.r.d.c.DelayedConsumerAnnotatedEdition : 当前时间:2022-08-25 17:24:52 订单取消订单号:100000005

案例源代码

https://gitee.com/SimpleWu/blogs-examples/tree/master/rabbitmq-delay-queue-case

RabbitMQ实现订单超时案例的更多相关文章

  1. day89:luffy:使用Celery完成我的订单超时取消&Polyv视频加密播放

    目录 1.我的订单超时取消 2.PoliV视频播放 1.我的订单超时取消 使用Celery完成超时取消功能 mycelery/order/tasks.py from mycelery.main imp ...

  2. PHP【Laravel】delayer基于redis的实现订单超时改变状态

    实现这个功能前你需要知道以下,不然可能会比较吃力:1.服务器的计划任务,shell脚本,或者你有宝塔自带的计划任务会方便很多.2.有所了解Redis.3.会写PHP业务逻辑. 好了进入在正题,这里使用 ...

  3. flink-----实时项目---day06-------1. 获取窗口迟到的数据 2.双流join(inner join和left join(有点小问题)) 3 订单Join案例(订单数据接入到kafka,订单数据的join实现,订单数据和迟到数据join的实现)

    1. 获取窗口迟到的数据 主要流程就是给迟到的数据打上标签,然后使用相应窗口流的实例调用sideOutputLateData(lateDataTag),从而获得窗口迟到的数据,进而进行相关的计算,具体 ...

  4. 订单超时、活动过期解决方案:php监听redis key失效触发回调事件

    Redis 的 2.8.0 版本之后可用,键空间消息(Redis Keyspace Notifications),配合 2.0.0 版本之后的 SUBSCRIBE 就能完成这个定时任务的操作了,定时的 ...

  5. SQL Server 连接超时案例一则

    上周六,一工厂系统管理员反馈一数据库连接不上,SSMS连接数据库报"连接超时时间已到.在尝试使用预登录握手确认时超过了此超时时间.......", 如下截图所示: 另外远程连接也连 ...

  6. 消息中间件RabbitMq的代码使用案例

    消费者: ---------------------- 构造初始化: public RabbitMqReceiver(String host, int port, String username, S ...

  7. Mapreduce 订单分组案例

    程序执行流程如下: map()-->getPartition()分区--->write()(序列化,每一行都顺序执行这三个方法)--->readFields()---->com ...

  8. 基于rabbitmq延迟插件实现分布式延迟任务

    承接上文基于redis,redisson的延迟队列实践,今天介绍下基于rabbitmq延迟插件rabbitmq_delayed_message_exchange实现延迟任务. 一.延迟任务的使用场景 ...

  9. Spring Boot(十三)RabbitMQ安装与集成

    一.前言 RabbitMQ是一个开源的消息代理软件(面向消息的中间件),它的核心作用就是创建消息队列,异步接收和发送消息,MQ的全程是:Message Queue中文的意思是消息队列. 1.1 使用场 ...

随机推荐

  1. Tensorboard SummaryWriter()

    import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvi ...

  2. 7. Docker CI、CD

    在上图这个新建的docker-compose.yml文件中把刚才的代码粘贴进去. 可把上述文件保存后,然后到/etc/ssh/sshd_config文件中更改下对应的端口号即可. 然后重新启动sshd ...

  3. Dubbo的基本使用

    Dubbo分为提供者和消费方  并且两者都要注册到ZK上 提供者 注解    @Service   这是dubbo包下的 消费组 注解    @Reference 远程注入 第一步导入依赖 <! ...

  4. NC16660 [NOIP2004]FBI树

    NC16660 [NOIP2004]FBI树 题目 题目描述 我们可以把由"0"和"1"组成的字符串分为三类:全"0"串称为B串,全&quo ...

  5. 论文阅读 GloDyNE Global Topology Preserving Dynamic Network Embedding

    11 GloDyNE Global Topology Preserving Dynamic Network Embedding link:http://arxiv.org/abs/2008.01935 ...

  6. P2575 高手过招 题解

    题目描述 我们考虑如何把问题转换成博弈论来求解. 我们对于每一行之前都加上一个空格. 设原来这一行的空格个数是 \(C\) ,那么此时空格个数变成 \(C + 1\) . 然后按照从左到右的顺序给每一 ...

  7. Intel的CPU系列说明

    至强可扩展系列是英特尔推出的新一代至强处理器系列,如今距离该系列推出几乎过去一年了.新的CPU并没有延续E系列的命名,英特尔将至强可扩展系列以金属命名,将该系列分为"铂金Platinum&q ...

  8. Airbnb的动态kubernetes集群扩缩容

    Airbnb的动态kubernetes集群扩缩容 本文介绍了Airbnb的集群扩缩容的演化历史,以及当前是如何通过Cluster Autoscaler 实现自定义扩展器的.最重要的经验就是Airbnb ...

  9. Ask.com用过什么名字?

    搜索引擎 Ask.com 曾是美国第三,世界第六大公网搜索引擎,仅次于 Google 搜索.Bing 和百度.NAVER.Yandex. Ask.com 曾经用过什么名字? Ask Jetson As ...

  10. CF665B Shopping

    CF665B Shopping 题目描述 Ayush is a cashier at the shopping center. Recently his department has started ...