前言

人间清醒

业务场景

用戶在购买商品的时候通常会预购然后没付款,没付款的订单通常会被设置一个自动超时时间如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. 解决python无法打开谷歌浏览器问题

    python+selenium 打开浏览器网页时可能会出现两种情况, 一.python 初次打开浏览器未进行安装浏览器控制插件, 二.python 能打开浏览器的情况下,突然有一天发现无法打开并报错新 ...

  2. BUUCTF-被嗅探的流量

    被嗅探的流量 提示告知是文件传输的流量,那进去过滤http流量包即可,找到一个upload目录的,并且是post方式即可,追踪http流即可发现flag

  3. 手把手教你实现一个Vue无限级联树形表格(增删改)

    前言平时我们可能在做项目时,会遇到一个业务逻辑.实现一个无限级联树形表格,什么叫做无限级联树形表格呢?就是下图所展示的内容,有一个祖元素,然后下面可能有很多子孙元素,你可以实现添加.编辑.删除这样几个 ...

  4. UiPath存在文本Text Exists的介绍和使用

    一.Text Exists的介绍 检查是否在给定的UI元素中找到了文本,输出的是一个布尔值 二.Text Exists在UiPath中的使用 1. 打开设计器,在设计库中新建一个Sequence,为序 ...

  5. js--js实现基础排序算法

    前言 文本来总结常见的排序算法,通过 JvavScript  来实现 正文 1.冒泡排序 算法思想:比较相邻两个元素的大小,如果第一个比第二个大,就交换它们.从头遍历到尾部,当一轮遍历完后,数组最后一 ...

  6. 52 条 SQL 语句性能优化策略,建议收藏

    本文会提到 52 条 SQL 语句性能优化策略. 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引. 2.应尽量避免在where子句中对字段进行nul ...

  7. halcon变量窗口的图像变量不显示,重启软件和电脑都没用

    有幸遇到halcon变量窗口的图像变量不显示,重启软件和电脑都没用这个沙雕问题,也是找了蛮久才发现解决办法特意记录一下. 这是正常情况下的窗口(左边)和图像变量不显示的窗口(右边): 解决方法: 鼠标 ...

  8. Collection子接口:List接口

    1. 存储的数据特点:存储序的.可重复的数据. 2. 常用方法:(记住)增:add(Object obj)删:remove(int index) / remove(Object obj)改:set(i ...

  9. 基于 Hexo 从零开始搭建个人博客(二)

    阅读本篇前,请先配置好相应的环境,请仔细阅读教程 基于 Hexo 从零开始搭建个人博客(一). 原文链接:基于 Hexo 从零开始搭建个人博客(二) 前言 博客搭建过程遇到任何问题,优先在本页面搜索, ...

  10. 2022徐特立科学营&BIT机器人队电控课程讲义

    目录 \(\cdot\)电控简介 \(\cdot\)认识单片机   什么是单片机   时钟-单片机的脉搏 \(\cdot\)外设及应用   GPIO   PWM   定时器   UART \(\cdo ...