本期目标

  • 了解延时队列的工作原理
  • 自己可以通过现有的工具软件来实现延时队列服务

延时队列应用场景

延时队列有很多应用场景,简单来说需要在未来某个特定时间去执行的任务,都可以采用延时队列。例如:

  • 下订单后 30 分钟未支付的释放订单。
  • 用户完成某个功能后,N 天推送个消息。
  • 当前我们的使用场景就属于用户被商家处理后,需要 2 天后给用户反馈消息。
  • ……

上面提到的这些场景如果不用延时队列都是可以处理的,例如轮训 DB 等,如果采用了延时队列会有更好的效果,除了以上的应用场景,还有很多,可以自己探索。

为什么要基于 Redis 去设计一个延时队列

已经很有一些不错的延时队列产品。例如 BeansTalk,但是我们这里只为教学目的,所以自己设计一套,基于 PHP 和 Redis 的代码简单,通过自己设计这个过程,可以增强一些概念和了解原理。

使用 RabbitMQ 死信方式存在的问题

  • 同一个队列内,数据延时时间不固定,会有阻塞问题。

涉及的名词解释

  • Queue 就是正常的队列
  • enqueue 入队
  • dequeue 出对
  • Buckt 延时队列数据的存储器
  • Scanner 扫描 Bucket,寻找符合条件的数据

Buckt 设计思路

  • 为每条消息生成唯一标识,ZSET 存储数据的延时排序 { value: uuid, score: delay_seconds}
  • Hash 存储具体对应的数据 { key : uuid, value : data}

扫描 Bucket 的 ZSET 内的数据,如果 delay_seconds 满足条件,根据 uuid 取出 Hash 内数据,发送到正常队列,删除 ZSET 和 Hash 内的元素。

为了提高效率,降低延时,对整个服务默认分了 4 个 Bucket,每个 Bucket 对应一个进程去扫描处理。文档如下图:

数据流程和文档 01 数据流程和文档 02

源码地址: http://github.com/outman