Redis list 实现消息队列可靠吗?那Redis实现可靠的消息队列方式是长什么样的


Redis list 实现消息队列可靠吗?那Redis实现可靠的消息队列方式是长什么样的

Redis 天然的消息队列存在的问题

如果直接使用Redis作为消息队列,可能都会用到这三种

  1. 单纯通过 redis list实现的缺点

    • 消息不保证被消费,消息被弹出队列后,没法记录消息状态,如果弹出队列后消费者服务不可用,那么这个消息可能就丢失了
    • 无法多组消费
    • 没有确认机制
  2. 通过发布订阅缺点
    • 消息持久性
    • 消息不保证被消费
    • 性能问题,基于推送,消费者方没法控制消费速度
    • 没有确认机制
  3. 通过Redis Stream
    • 对于用redis来做队列的场景,基本上满足

Redis如何实现比较可靠的消息消费机制,除了使用Redis Stream之外

问题:如果单单使用一个list作为消息队列来操作,那么这既没有消息消费确认机制,也没有消费后的记录 所以需要解决可靠性,就必须引入消息状态和确认机制,以及消息历史记录 另外redis有一点没法跟kafka,rabbitmq这些专门的消息队列相比,就是可用性

  1. 消息状态
    • 比如 等待消费,消费中,消费完成,消费失败 这几个状态。
    • 如果需要实现这几个状态,我觉得可以通过每个状态都需要一个数据结构来维持,比如等待消费使用list,消费中使用zset,消费完成也是用zset
    • 消费中操作: 从待消费列表pop出一个,然后压入消费中队列
    • 消费完成操作:从消费中队列取出来然后放到消费完成队列
    • 消费失败操作:将其他状态里取出,放入到消费失败的队列中
    • 消费中,消费完成,消费失败可以使用zset来存储,消息具体内容也可以单独分离出来到key-value中
    • 以上的状态转移操作,如果使用Redis命令一条条执行就无法做到原子性,为了保证其原子性,一定要使用lua脚本来处理
  2. 通过多个结构来做消息状态转移,那么消息记录也可以被持久化,但是长此以往,已完成的消息会堆积成山,解决这个问题,如果觉得不重要,直接删了;稳妥点迁移到数据仓库即可

疑问

  1. lua为何可以保持Redis系列操作的原子性
    • 按照Redis的架构来说,Redis执行命令是通过单线程,也就是说所有的请求最后到这个线程就得排队顺序执行,lua脚本在这就是一个整体一个批次顺序执行,可以保证是顺序执行
  2. 那如果其中某个步骤执行失败如何回滚
    • 按照Redis官方来说,reids是不会出错的,所以不回滚,错在你,一定是你的代码有问题~
    • 看laravel的lua脚本,基本上也是对操作没有进行判断的

参考

  1. laravel队列 laravel Redis queue
  2. 队列管理增强 laravel horizon
  3. php-enqueue
  4. php-rsmq