for循环延时_基于Redis实现简易消息队列和延时消息队列

本文介绍如何使用Redis的基本数据类型list和zset实现简易的消息队列及延时队列。对于消息队列,通过lpush/rpop命令组合,利用blpop实现阻塞读取;对于延时队列,则利用zset的消息处理时间作为score,结合多线程轮询获取到期任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.背景

有时候,我们只是需要在系统内或两系统间将任务做延时异步处理,如果因为这个需求引入消息队列中间件,比如Kafka、Rabbitmq,未免有点杀鸡用牛刀。在消息可靠性要求不那么高的情况下,使用Redis基本数据类型list或zset,可以轻松满足我们的需求.

2.list实现简易消息队列

如下图所示,简易消息队列使用到list数据结构以及相关命令lpush、rpop或者rpush、lpop。

5f8bb4663f4331eb5f21efef342626cb.png


这里提一个问题,当队列为空怎么办?

方案1:客户端sleep后轮询。

Thread.sleep(1000)

方案1的问题是,由于客户端不知道消息什么时候到来,只能去猜测休眠时间,其实还是很不靠谱,这样做消息无法及时处理,如果减少休眠时间,则又会导致空转增加CPU的消耗。

方案2:使用blpop/brpop进行阻塞读

redis中blpop可以实现list的阻塞操作,客户端连接在list没有数据的情况下会进行阻塞。

这里大家可能会有一个疑问:redis本身是一个单线程服务,如果阻塞客户端一直保持着跟服务器的链接,会不会阻塞其他命令的执行呢?

答案显然是不会,在redis server中有两个循环:IO循环和定时循环。

  • 在IO循环中,redis完成客户端连接应答、命令请求处理和命令处理结果回复等。
  • 在定时循环中,redis完成过期key的检测等。

redis一次连接处理的过程包含几个重要的步骤:IO多路复用检测套接字状态,套接字事件分派和请求事件处理。redis在blpop命令处理过程时,首先会去查找key对应的list,如果存在,则pop出数据响应给客户端。否则将对应的key push到blocking_keys数据结构当中,对应的value是被阻塞的client。当下次push命令发出时,服务器检查blocking_keys当中是否存在对应的key,如果存在,则将key添加到ready_keys链表当中,同时将value插入链表当中并响应客户端。

服务端在每次的事件循环当中处理完客户端请求之后,会遍历ready_keys链表,并从blocking_keys链表当中找到对应的client,进行响应,整个过程并不会阻塞事件循环的执行。所以, 总的来说,redis server是通过ready_keys和blocking_keys两个链表和事件循环来处理阻塞事件的。

代码实现如下。

生产者

public 

消费者

public 

测试代码

public 

输出:

message_queue

andy

message_queue

jerry

message_queue

forest

3.延时队列实现

延时队列可通过zset来实现,消息的处理时间作为score,然后通过多线程轮询获取到期的score任务。

API定义

d931f10b2d0d894032c0f29d0c7799f9.png

代码实现

public 

测试代码

public 

优化

将zrangebyscore和zrem通过lur脚本放到服务端进行原子化操作,避免不必要争抢。

The end.

转载请注明来源,否则严禁转载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值