Redis具有过期监听的功能
于是有人拿他实现订单关闭,但是这个方案并不完美。
电商、支付系统
1.支付单:一般都是先创建订单(支付单),再给用户一定时间进行支付,如果没有按时支付的话,就把之前的订单取消掉。
2.到期自动收货
3.超时自动退款
4.下单后自动发送短信
1.被动关闭(骚操作)
用户查看订单时,做更新订单操作
2.定时任务
定时任务去扫描库,做更新订单操作。
缺点:1.时间不精确 2.无法处理大订单量 3.对数据库造成压力 4.分库分表问题
JDK 延时队列
DelayQueue 是一个无界的阻塞队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走;插入和删除的时间复杂度O(nlog(n))
缺点:订单量大,会造成OOM,基于JVM内存的,一旦机器重启了,造成数据丢失。
Netty 时间轮
时间轮,将插入和删除操作的时间复杂度降为O(1).
时间轮,一个环形结构,像钟表一样被分为多个slot。每一个slot代表一个时间段,每个slot中可以存放多个任务(使用链表结构保存到期的所有任务)。时间轮通过一个指针随着时间一个个slot转动,并执行slot中所有到期任务。
基于Netty的HashedWheelTimer可以快速实现一个时间轮。基于JVM内存的。
Kafka的时间轮
RocketMQ延迟消息
商业版支持任意延时,开源版支持1s 5s 10s 30s 1m 2m 3m ... 10m 20m 30m 1h 2h
RabbitMQ 死信队列
当一条正常消息,因为存活时间(TTL过期)、队列长度超限、被消费者拒绝等原因无法被消费时,它能被重新发送到死信队列中(exchange)
我们给一个消息设定TTL,然后不消费这个消息,等他过期,过期后就会进入到死信队列,然后我们再监听死信队列的消息,进行消费
RabbitMQ rabbitmq_delayed_message_exchange 插件(3.6.12开始支持)
创建x-delayed-message类型的队列。基于插件的方式,消息并不会立即进入队列,而是先把他们保存在一个基于Erlang开发的Mnesia数据库中,然后通过一个定时器去查询需要被投递的消息,再把他们投递到x-delayed-message队列中。
Redis 过期
redis.conf 配置文件中:notify-keyspace-events 开启过期监听(Redis不保证Key在过期的时候被立即删除)
Redis zset
Zset是一个有序集合
每一个元素(member)都关联了一个score,可以通过score排序来取集合中的值
定时任务:每次获取"当前时间 > score"延时任务;分布式时会出现获取同一个订单号
Redisson
Redisson是一个在Redis基础上实现的框架,不仅提供了一系列分布式的java常用对象,还提供了许多分布式服务。
Redission 定义了分布式延时队列:RDelayedQueue
总结
Redisson > RabbitMQ 插件 > RabbitMQ 死信队列 > RocketMQ 延时消息 > Redis zset > Redis 过期监听> Kafka 时间轮 > 定时任务 > Netty 时间轮 > JDK 自带DelayQueue > 被动关闭