Redis Zset 实现延时队列

前言

延时队列是一种特殊类型的队列,其中的消息或任务会被延迟处理,直到特定的时间到达。

Redis中,可以使用ZSET(有序集合)实现延时队列。

关于ZSET的内容,可以看这里 ——> Redis 中的 ZSET

一、举例

比方说,现在有一个业务场景:用户创建了一个表单,该表单将于一分钟后自动提交,如何用ZSET实现呢?

下面会用python代码实现一个这样的功能。

二、步骤

1、连接到 redis

首先获取一个 redis 实例对象。

定义一个 ZSET :用于实现延时队列。

定义一个 HASH :用于存储表单信息。

# redis 相关配置
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0
REDIS_PASSWORD = 'localhost'
​
# 获得一个 redis 实例对象
r = redis.client.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, charset="utf-8", decode_responses=True, password=REDIS_PASSWORD)
​
# 定义一个 HASH 和 一个 ZSET 实现功能
REDIS_JOB_QUEUE = 'JOB:DELAY-QUEUE'
REDIS_JOB_HASH = 'JOB:HASH'

2、推入延时队列逻辑

写一个推入延时队列的逻辑,当创建出表单时,将表单 id 和 创建内容放到 HASH 中;再将 表单 id 和 一分钟后的时间戳放入 ZSET 中,实现延迟队列。

def push_to_delayed_queue(formId, create_msg):
    key = formId
    value = {'formId': formId, 'data': created_record}
​
    # 序列化value为JSON字符串
    value_json = json.dumps(value)
​
    # 将 formId 作为 key,将 formId, data 放在字典中作为 value,存入一个 hash 中
    r.hset(REDIS_JOB_HASH, mapping={key: value_json})
​
    # 将 formId 作为 member,一分钟以后的时间戳作为 score,存入一个 zset 中
    score = int(time.time()) + 60
    r.zadd(REDIS_JOB_QUEUE, {key: score})

3、消费延时队列逻辑

用一个 while True 去循环检查延时队列,当发现有数据的时间戳小于当前时间戳时,将该数据取出,进行消费。同时要注意并发情况,最终只能有一个进程抢到消息。

并且防止循环中断,用 try 去捕获可能的异常。

# 返回一个 redis 示例对象
def issue_redis_client():
    return redis.client.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, charset="utf-8", decode_responses=True, password=REDIS_PASSWORD)
​
def loop():
    logging.info("start delay_queue loop")
    rc = issue_redis_client()
    while True:
        try:
            # 取已达到执行时间的任务列表,但是每次列表里只取一条数据
            values = rc.zrangebyscore(REDIS_JOB_QUEUE, 0, time.time(), start=0, num=1)
            if not values:
                # 如果没有到达执行时间的任务就休息 1 秒
                time.sleep(1)
                continue
            # 获取第一条记录,这是一个二进制字符串
            value = values[0]
​
            # 试图从 zset 中移除这条记录
            success = rc.zrem(REDIS_JOB_QUEUE, value)
​
            # 因为有多进程并发的可能,最终只会有⼀个进程可以抢到消息,如果移除成功,则该记录没有被其他进程处理
            if success:
                # 处理消息
                handle_msg(value)
        except Exception as e:
            print(f"An error occurred: {e}")
            # 这里根据需要实现错误日志记录和处理逻辑
            time.sleep(1)

4、处理消息逻辑

处理消息的逻辑。

根据 表格 id 从 HASH 将创建信息提出来,然后提交表单。

def handle_msg(msg):
    key = msg
​
    value_json = r.hget(REDIS_JOB_HASH, key)
    
    r.hdel(REDIS_JOB_HASH, key)
​
    if value_json:
        # 反序列化
        value = json.loads(value_json)
        # todo: 根据表单内容提交的方法,此处省略

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值