参考Redis 消息队列的三种方案(List、Streams、Pub/Sub) - 知乎 (zhihu.com)
各种开源的 MQ 已经足够使用了,为什么需要用 Redis 实现 MQ 呢?
优点:
- 简单轻量:Redis是一个内存中的数据存储系统,具有轻量级 和简单 的特点。相比较专门的消息队列系统,使用Redis作为消息队列不需要引入额外的组件和依赖,可以减少系统的复杂性。
- 速度快:由于Redis存储在缓存 中,它具有非常高的读写性能 。这对于需要低延迟的应用程序非常有优势。
- 多种数据结构支持 :Redis提供了丰富的数据结构,如列表、发布/订阅、有序集合等。这使得Redis在处理不同类型的消息和任务时更加灵活。
- 数据持久化:Redis可以通过将数据持久化到磁盘来提供数据的持久性。这意味着即使Redis重启,之前的消息也不会丢失。
- 广泛的应用场景:Redis不仅可以用作消息队列,还可以用作缓存、数据库、分布式锁等多种用途。如果你的应用程序已经使用了Redis,那么使用Redis作为消息队列可以减少技术栈的复杂性。
缺点:
- 缺少一些高级特性:相对于专门的消息队列系统,Redis在消息队列方面的功能可能相对简单。例如,它可能缺乏一些高级消息传递功能,如消息重试、消息路由、持久化消息等。
- 可靠性和一致性:Redis的主要设计目标是提供高性能和低延迟,而不是强一致性和高可靠性。在某些情况下,Redis可能会丢失消息,或者在出现故障时可能无法提供持久性保证。
应用场景:
适用于简单的中小型项目 如果功能简单,访问量并不大 可以考虑
如果你的应用程序对可靠性和高级功能有严格要求,并且需要处理大量的消息和复杂的消息路由,那么使用专门的消息队列系统可能更合适。
消息队列有什么特点?
- 三个角色:生产者、消费者、消息处理中心
- 异步处理模式:生产者 将消息发送到一条 虚拟的通道 (消息队列)上,而无须等待响应。消费者 则 订阅 或是 监听 该通道,取出消息。两者互不干扰,甚至都不需要同时在线,也就是我们说的 松耦合
- 可靠性:消息要可以保证不丢失、不重复消费、有时可能还需要顺序性的保证
Redis实现消息队列有几种方法?或者说有几种模型?
- List
- Streams
- 发布、订阅(pub/sub) 模式
List 实现消息队列
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。所以常用来做异步队列 使用。将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另一个线程从这个列表中轮询数据进行处理。
使用 List 实现消息队列是一种点对点 的消息模型,并且消息一经发送出去,便从队列里删除 。如果由于网络原因消费者没有收到消息,或者消费者在处理这条消息的过程中崩溃了,就再也无法还原出这条消息。因此需要消息确认机制。
消息确认机制:从一个 list 中获取消息的同时把这条消息复制到另一个 list 里(可以当做备份),而且这个过程是原子的。这样我们就可以在业务流程安全结束后,再删除队列元素,实现消息确认机制。
订阅与发布实现消息队列
我们都知道消息模型有两种
- 点对点:Point-to-Point(P2P)
- 发布订阅:Publish/Subscribe(Pub/Sub)
List 实现方式其实就是点对点的模式,下边我们再看下 Redis 的发布订阅模式(消息多播 ),这才是"根正苗红"的 Redis MQ
"发布/订阅"模式同样可以实现进程间的消息传递,其原理如下:
"发布/订阅"模式包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或者多个频道(channel),而发布者可以向指定的频道(channel)发送消息,所有订阅此频道的订阅者都会收到此消息。Redis 通过 PUBLISH 、 SUBSCRIBE 等命令实现了订阅与发布模式。
发布时若客户端不在线,则消息丢失,不能寻回 ;不能保证每个消费者接收的时间是一致的;若消费者客户端出现消息积压,到一定程度,会被强制断开,导致消息意外丢失。通常发生在消息的生产远大于消费速度时;Pub/Sub 模式不适合做消息存储,消息积压类的业务,而是擅长处理广播,即时通讯,即时反馈的业务。
Streams 实现消息队列
Redis 发布订阅 (pub/sub) 有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。而且也没有 Ack 机制来保证数据的可靠性,假设一个消费者都没有,那消息就直接被丢弃了。
Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
它就像是个仅追加内容的**消息链表**,把所有加入的消息都串起来,每个消息都有一个唯一的 ID 和对应的内容。而且消息是持久化的。
Redis如何保证消息有序
Redis 是一个开源的内存数据存储系统,它被广泛用于实时应用程序中,包括消息队列系统。在使用 Redis 作为消息队列时,消息的顺序性是一个重要的问题。下面是一些方法,可以帮助 Redis 保证消息的顺序:
-
使用单线程处理消息:Redis 是单线程的,这意味着它可以逐个处理消息,确保消息的顺序性。当消息到达 Redis 时,可以依次处理它们,而不用担心并发操作导致顺序混乱。
-
使用有序集合(ZSet):有序集合是 Redis 中的一种数据类型,它按照元素的分数进行排序。可以将消息的序列号作为元素的分数,将消息内容作为元素的值。通过使用有序集合,可以按照序列号的顺序存储消息,并通过分数范围查询来获取消息。
-
使用自增序列号:可以为每个消息分配一个唯一的自增序列号,并将消息放入 Redis 的列表或有序集合中。当需要获取消息时,可以按照序列号顺序遍历列表或有序集合,以保证顺序性。
-
使用 Redis 事务:Redis 支持事务操作,可以将多个操作封装在一个事务中。通过使用事务,可以确保多个消息的连续操作在同一个事务中执行,从而保证消息的顺序性。
-
使用 PUB/SUB 模式:Redis 提供了发布订阅(PUB/SUB)模式,可以实现一对多的消息传递。在发布消息时,可以指定消息的频道(channel),订阅者按照订阅顺序接收消息。通过使用 PUB/SUB 模式,可以保证订阅者按照顺序接收消息。
需要注意的是,尽管 Redis 可以通过上述方法保证消息的顺序性,但在分布式环境下,由于网络延迟和节点故障等原因,仍然可能发生消息的顺序混乱。因此,在设计使用 Redis 作为消息队列时,需要综合考虑系统的可靠性和性能需求,并采取适当的策略来保证消息的顺序。