延迟队列

比如要实现30分钟未支付订单取消,量少的时候可以用数据库轮训的方式,但是数据量大的话,轮训的并发和准确性就不可靠,这个时候可以用延迟队列来解决这个问题

延迟队列的实现

  • [RabbitMQ] RabbitMQ通过RabbitMQ Delayed Message Plugin可支持延迟队列
  • [Redis] Redis的Sorted Set可被用于实现简单的延迟队列。利用Redis的Lua支持我们也可以将基建设成一个功能全面的延迟队列服务。
  • [Redis + RabbitMQ] redis用来消费任务不可靠,没有ack机制支持,如果消费的脚本出现异常,那这个任务就有可能丢失,redis可以仅仅用来做延迟,取到消息后直接推到 RabbitMQ 或者 Kafka 进行消费

Redis 实现延迟队列

Sorted Set是一个有序的集合,集合内元素的排序基于其加入集合时指定的score。通过ZRANGEBYSCORE命令,我们可以取得score在指定区间内的元素。将集合中的元素做为消息,score视为延迟的时间,这便是一个延迟队列的模型。

生产者通过ZADD将消息发送到队列中:

ZADD delay-queue 1520985600 "publish article"

消费者通过ZRANGEBYSCORE获取消息。如果时间未到,将得不到消息;当时间已到或已超时,都可以得到消息:
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
min 和 max 可以是 -inf 和 +inf ,这样一来,你就可以在不知道有序集的最低和最高 score 值的情况下,使用 ZRANGEBYSCORE 这类命令。

127.0.0.1:6379> ZRANGEBYSCORE delay-queue -inf 1520985599
(empty list or set)
127.0.0.1:6379> ZRANGEBYSCORE delay-queue -inf 1520985600 WITHSCORES
1) "publish article"
2) "1520985600"
127.0.0.1:6379> ZRANGEBYSCORE delay-queue -inf 1520985601 WITHSCORES
1) "publish article"
2) "1520985600"

使用ZRANGEBYSCORE取得消息后,消息并没有从集合中删出。需要调用ZREM删除消息:

127.0.0.1:6379> ZREM delay-queue "publish article"

美中不足的是,消费者组合使用ZRANGEBYSCORE和ZREM的过程不是原子的,当有多个消费者时会存在竞争,可能使得一条消息被消费多次。此时需要使用Lua脚本保证消费操作的原子性:

local message = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1], 'WITHSCORES', 'LIMIT', 0, 1);
if #message > 0 then
  redis.call('ZREM', KEYS[1], message[1]);
  return message;
else
  return {};
end

  转载请注明: 南归 延迟队列

 上一篇
对ID进行隐藏 对ID进行隐藏
Hashid不希望对外暴露有规则的数据索引,比如用户 ID 、媒体资源 ID 、商品 ID 、订单号、注册码、优惠码等,防止爬虫侵扰。那就将ID编码咯。 解决方案 vinkla/hashids composer 懂的哈可以自己加salt,这
2019-01-11
下一篇 
Redis 分布式锁 Redis 分布式锁
实现一个分布式锁定,我们至少要考虑它能满足一下的这些需求: 互斥,就是要在任何的时刻,同一个锁只能够有一个客户端用户锁定. 不会死锁,就算持有锁的客户端在持有期间崩溃了,但是也不会影响后续的客户端加锁 谁加锁谁解锁,很好理解,加锁和解锁的
2018-12-04
  目录