1、JDK提供的DelayQueue
一种支持延时获取元素的无界阻塞队列。
内部持有一个PriorityQueue,每个对象都被放入了这个队列,队列中的对象按照优先级进行了排序,队列头部是最先会超时的对象。
take方法会一直阻塞,直到队列头部的对象超时后才可以被取出。
2、Redis sorted set(ZSET)
实现思路大同小异。以过期时间的时间戳作为score。
生产者将消息内容作为member,时间戳作为score调用ZADD来生产消息;
消费者用ZRANGEBYSCORE命令获取N秒之前的数据进行轮询处理,使用min和max向前推N秒来卡延时的消息。
3、RabbitMQ
rabbitMQ中可以对Message设置 x-message-ttl(TTL = Time To Live)来控制消息的生存时间。超时以后消息变为dead letter(死信)。
同时,RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。
利用这样的特性,设置两个队列,A队列无消费者,生产者向该队列发送消息,消息设定TTL;同时设定A中出现死信以后将消息转发到B队列;B队列使用正常设定即可,所有消费者从B读取消息。
即可完成延时时间为TTL的延时队列。
4、Redis 过期回调
在Redis中开启监听key是否过期的事件,一旦key过期会触发一个callback事件。
有点类似MQ的队列事件监听。
5、Kafka
Kafka有专门用于实现延迟功能的定时器(SystemTimer)。底层使用的是时间轮(TimingWheel)模型(环形队列,下面挂接双向链表)。
操作效率非常高。JDK的DelayQueue插入和删除操作的平均时间复杂度为O(nlog(n)),而Kafka基于时间轮可以将插入和删除操作的时间复杂度都降为O(1)。