Redis实现异步队列

使用Redis实现任务队列首先想到的就应该是Redis的列表类型List,这是因为Redis中的列表类型是由双向链表实现的,符合队列的功能。

实现其实很简单:

  • 非阻塞实现

生产者使用LPUSH 将任务加入队列,消费者使用RPOP将任务移除队列,一个先入先出的队列就实现了:

//生产者只需将数据LPUSH到队列中
127.0.0.1:6379> LPUSH queue task
(integer) 1
127.0.0.1:6379> LRANGE queue 0 -1
1) "task"
//消费者从队列中RPOP任务,如果为空则直接返回nil。
127.0.0.1:6379> RPOP queue
"task"

这个实现很简单,但是当然也有缺点,那就是当队列没有数据时,也会执行pop操作,此时可以使用阻塞式队列

  • 阻塞式实现
127.0.0.1:6379> BLPOP queue 0   //0表示无限期等待,大于0的数表示阻塞时间
/若当队列为空则,则消费者的POP操作会处于阻塞状态。
//生产者将数据LPUSH到队列中
127.0.0.1:6379> LPUSH queue task
(integer) 1

//消费者立刻“消费”,取出task。
127.0.0.1:6379> BLPOP queue 0
1) "queue"
//此时生产者产生数据,消费者则获取到数据,退出阻塞
2) "task"
//阻塞时间,若超时,会直接返回nil
(13.38s)

这样的阻塞实现更像一个队列了

  • 优先队列实现

如果有多个队列,消费者优先消费优先队列的数据:

127.0.0.1:6379> LPUSH queue1 first 
(integer) 1
127.0.0.1:6379> LPUSH queue2 second
(integer) 1

//当两个键都有元素时,按照从左到右的顺序取第一个键中的一个元素
127.0.0.1:6379> BRPOP queue1 queue2 0
1) "queue1"
2) "first"
  • 订阅发布

前面三个队列的缺点就是:生产者往其中加入了一个任务,这个任务只能被一个消费者接受,能不能像MQ那样,发布一个,多个消费者都能收到呢?是可以的,这就要使用到发布/订阅(pub/sub)

127.0.0.1:6379> PUBLISH channel1.1 task  //往channel1.1这个频道发布一个task
(integer) 0 //有0个客户端收到消息,因为还没有绑定

127.0.0.1:6379> SUBSCRIBE channel1.1   //订阅channel1.1这个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"  //"subscribe"表示订阅成功
2) "channel1.1" //订阅成功的频道
3) (integer) 1  //表示频道下,订阅客户端的数量,此时只有自己
//此时发布者发布一个信息task,订阅者会收到如下消息:
1) "message"    //接收到消息
2) "channel1.1" //产生消息的频道
3) "task"       //消息的内容
//当订阅者取消订阅时:
127.0.0.1:6379> UNSUBSCRIBE channel1.1
1) "unsubscribe"    //成功取消订阅
2) "channel1.1" //取消订阅的频道
3) (integer) 0  //当前订阅客户端的数量

另外,PSUBSCRIBE可以批量订阅指定的模式,其中?代表一个字符,*代表任意字符,包括0

127.0.0.1:6379> PSUBSCRIBE channel1.*  //订阅这个模式的频道,以channel1.开头的频道都会被订阅到
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel1.*"
3) (integer) 1
//等待发布者发布消息
127.0.0.1:6379> PUBLISH channel1.1 test1.1
(integer) 1 //发布者在channel1.1上发布消息

//客户端中:
1) "pmessage"   //表示通过PSUBSCRIBE命令订阅而收到的
2) "channel1.*" //表示订阅时使用的通配符
3) "channel1.1" //表示收到消息的频道
4) "test1.1"        //表示消息内容

127.0.0.1:6379> PUBLISH channel1.2 test1.2
(integer) 1 //发布者在channel1.2发布消息

1) "pmessage"
2) "channel1.*"
3) "channel1.2"
4) "test1.2"

127.0.0.1:6379> PUBLISH channel1.3 test1.3
(integer) 1 //发布者在channel1.3发布消息

//客户端中
1) "pmessage"
2) "channel1.*"
3) "channel1.3"
4) "test1.3"

127.0.0.1:6379> PUNSUBSCRIBE channal1.*
1) "punsubscribe"   //退订成功
2) "channal1.*" //退订规则的通配符
3) (integer) 0  //表示当前订阅客户端的数量
在高并发场景下,流量削峰和缓冲是系统设计中的关键问题。Redis 作为一种高性能的内存数据库,可以通过其数据结构与功能实现有序异步队列,从而有效应对突发流量并缓解后端系统的压力。 ### Redis 实现有序异步队列 Redis 提供了多种适用于消息队列的数据结构,包括 List、Pub/Sub 和 Stream。其中,Stream 是 Redis 5.0 引入的新特性,具备持久化、消息确认、消费者组等高级功能,更适合构建有序异步队列[^4]。 - **使用 List 结构**:List 是最基础的队列实现方式,通过 `LPUSH` 和 `BRPOP` 等命令实现先进先出(FIFO)队列。 - **使用 Stream 结构**:Stream 支持多播、消息持久化和消费确认机制,适合对消息可靠性有较高要求的场景。 ```python # 示例:使用 Redis Stream 实现阻塞式消息读取 while True: message = redis_client.xread({'mystream': '$'}, count=1, block=2000) if message: # 处理消息 handle_message(message) ``` ### 流量削峰机制 在秒杀或抢购等高并发业务中,瞬时请求可能远超系统处理能力。通过将请求写入 Redis 队列,可以将突发流量“拉平”,使后端服务以稳定速率处理任务,避免系统崩溃或响应延迟[^3]。 - **削峰策略**: - 前端接收所有请求,写入 Redis 队列。 - 后端服务按照自身处理能力从队列中取出任务进行处理。 - 可结合限流策略,防止队列无限增长。 ### 缓冲机制设计 为了进一步提升系统的稳定性,可以在 Redis 消息队列之上引入缓冲层: - **双层队列机制**:前端使用 Redis Stream 接收消息作为第一层缓冲,后端服务再将任务写入本地队列作为第二层缓冲。 - **自动扩缩容**:根据队列长度动态调整消费者数量,提高资源利用率。 - **持久化与重试机制**:对于关键任务,可将未成功处理的消息重新放回队列,并记录日志以便后续分析。 ### 方案选型建议 | 数据结构 | 优点 | 缺点 | 适用场景 | |---------|------|------|----------| | List | 简单易用,支持阻塞操作 | 不支持消息确认和持久化 | 轻量级任务队列 | | Pub/Sub | 实时性强,支持广播 | 消息不持久化,无法保证送达 | 即时通知类场景 | | Stream | 支持持久化、确认机制、消费者组 | 相对复杂 | 对可靠性要求高的队列 | 综合来看,若需要实现一个具备有序性、可靠性和削峰能力的消息队列系统,推荐使用 Redis Stream 方案。它不仅能够满足高并发下的缓冲需求,还能提供完整的消费确认机制,确保消息不丢失[^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值