Python Redis Stream 消息队列 消费组

项目有用到消息队列来消费不断新增的任务,本来看到RedisPub Sub就没准备用kafka了,后来看了下Redis 5.0新加的Stream,感觉刚好符合项目要求,看下文档就直接用上了,类似一个简单的无限轮询,诸君自取

stream.py

import redis
import sys
from utils.logger import logger
import time

def process_message(id, fields):
    logger.info(f"consumer info: [{consumer_name}] {id} = {fields}")


def stream_message():
    """stream and groups info"""
    logger.info(f'stream info: {r.xinfo_stream(stream_name)}')
    logger.info(f'groups info: {r.xinfo_groups(stream_name)}')

# 这里我把写成从外部命令获取一个消费者名字,方便启动多个消费者
# 所以命令要加上一个消费者参数,如: python stream.py consumer-0
ARGV = sys.argv[1:]
if len(ARGV) == 0:
    logger.error("Please specify a consumer name")
    sys.exit()

consumer_name = ARGV[0]
stream_name = "convert"
group_name = "convert-group"

pool = redis.ConnectionPool(
    host='localhost', port=6379, decode_responses=True)  # decode_responses=True 这样写存的数据是字符串格式
r = redis.StrictRedis(connection_pool=pool)

def mock_redis():
    """mock data"""
    if r.exists(stream_name):
        r.delete(stream_name)
    r1 = r.xadd(stream_name, {'name': 'jack'})
    r2 = r.xadd(stream_name, {'name': 'Tom'})
    r3 = r.xadd(stream_name, {'name': 'Will'})

    # 创建 stream group
    r.xgroup_create(stream_name, group_name, id=0)  # 0 从开始消费, $ 从尾部消费
    logger.info(f'stream info: {r.xinfo_stream(stream_name)}')

mock_redis()  # 这里记得是在模拟数据哦,自取

lastid = '0-0'  # 一开始从 0 读取历史记录, 以防止服务崩溃遗漏

logger.info(f"Consumer {consumer_name} starting...")
stream_message()
check_backlog = True
while True:
    if check_backlog:
        consumer_id = lastid
    else:
        consumer_id = '>'

    # block 0 时阻塞等待, 其他数值表示读取超时时间
    items = r.xreadgroup(group_name, consumer_name, {stream_name: consumer_id}, block=0, count=1)  

    if not items:  # 如果 block 不是 0或者为空, 会需要这段
        logger.info("Timeout!")
        stream_message()
        time.sleep(3)  # 空值等待 3s
        continue
    elif len(items[0][1]) == 0:
        check_backlog = False

    for id, fields in items[0][1]:
        process_message(id, fields)
        try:
            # 0 / 0  # 这个模拟处理崩溃
            # 这里是你要做的事情,封一个函数这里调用即可
            pass
        except Exception as e:
            logger.exception(e)
            continue
        finally:
            lastid = id  # 无论是出错还是正常执行完毕,都要去读取下一个,否则可能会无限循环读取处理报错的数据
        r.xack(stream_name, group_name, id)
    time.sleep(2)  # 间隔时长,自取

运行命令:
python stream.py consumer-0

命令传入了一个消费者名称,方便启动多个,名称诸君随意
代码引入的日志器是这篇文章里的,Python logging config (看这篇就够了),代码是 Copy 拿走直接用的

对你有所帮助就点个赞吧:>

你可能感兴趣的:(博客,Python,redis,队列,python,消息队列,stream)