基于Redis的分布式任务调用框架实现

背景

Redis 5.0+版本后,Stream的支持可以使得Redis在轻度场景,作为消息队列使用。最近尝试借用该功能,实现一个基于消息调用的分布式任务框架的Demo,记录下学习内容。

主要功能

Agent+Redis-Stream消息队列,以Agent节点作为消息任务执行端,通过Redis消息传递调用:

  1. Agent节点上下线
  2. Agent的异步调用
  3. Agent的同步调用
  4. 调用消息(任务)的追踪和查询

Agent上线

简单Agent的上下线通过注册带有过期时间的节点HASH信息,实现Agent上报。通过不断地对HASH key续约,达到Agent节点持续在线的目的。基于该方式,Agent进行上报的同时,创建节点HASH Key的索引SET集合。创建索引集合的好处是无需进行全局Key扫描,即可查看所有Agent的在线离线状态。

如下图,本地测试使用SET集合作为节点的索引存储,

Agent异步调用

分析

我们先梳理异步调用的大致步骤:

1、向目标Agent端Stream发送异步调用消息

2、目标端读取Stream消息、并着手处理任务(消息)

3、目标端XACK消息,并将处理结果(HASH消息)回写,将HASH Key同步到索引列表

演示

1、发送 install_rpm的消息给节点 ali-master端,消息id: 7403288395704700928-1

2、Agent ali-master读取消息后,开始处理任务。消息此时进入stream中的pending队列

3、节点ali-master处理完消息并确认

4、查看所有的响应的任务列表

5、原生数据展示,queue:reply 为节点处理完成的消息的ID的索引列表

Agent同步调用

同步调用需要实时等待对方处理消息的结果。为防止无限制等待或者对端异常,至少需要添加超时时间。

高效的做法可通过监听 rpc:reply:message-id 的应答消息HASH Key事件。

演示效果(同步调用2s后返回调用结果):

代码对比如下:

python 复制代码
def cast(self, rpc_message: RPCMessage) -> str:
    """
    异步调用
    """
    redis_conn.xadd(
        self.req_stream, rpc_message.rpc_params, id=rpc_message.id
    )
    return f"{rpc_message.id}"


def call(self, rpc_message: RPCMessage, timeout: int = 30):
    """
    同步调用,基于键空间通知机制
    """
    reply_key = self.reply_key(str(rpc_message.id))
    # 订阅哈希键的事件
    pattern = f'__keyspace@0__:{reply_key}'
    pubsub = redis_conn.pubsub()
    pubsub.psubscribe(pattern)
    
    redis_conn.xadd(self.req_stream, rpc_message.rpc_params, id=rpc_message.id)

    start_at, elapsed_time = time.time(), 0
    try:
        while elapsed_time < timeout:
            msg = pubsub.get_message(timeout=10)
            elapsed_time = time.time() - start_at
            if msg is None:
                continue
                # 检查是否是 hset 相关事件
                if msg['type'] == 'pmessage' and msg['data'] in ['hset', 'hmset', 'set']:
                    reply_data = redis_conn.hgetall(reply_key)
                    return reply_data
    except Exception as e:
        ...
    finally:
        pubsub.unsubscribe(pattern)
    raise TimeoutError(f"{rpc_message.id} timeout after {timeout} seconds")

消息跟踪和查询

相对于grpc方式,Agent+消息队列的方式,能方便地实现消息跟踪、查询。

如果使用Redis做消息队列,需要考虑如下建议:

  1. 开启Redis AOF持久化
  2. 已读取的消息,可在stream中的pending列表中查到
  3. 已处理的消息类型为HSET,并添加消息ID到索引 reply:queue:agent
  4. 可通过reply:queue 查看历史处理的消息
  5. 可根据stream pending列表遍历,查看堆积的消息,看是否重新投递、或者清理
  6. 对于多个redis操作,可尝试使用事务方式执行。

结束

不知道写哈了,结束吧

相关推荐
倔强的石头_7 小时前
kingbase备份与恢复实战(二)—— sys_dump库级逻辑备份与恢复(Windows详细步骤)
数据库
jiayou642 天前
KingbaseES 实战:深度解析数据库对象访问权限管理
数据库
李广坤2 天前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
初次攀爬者3 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
爱可生开源社区3 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1774 天前
《从零搭建NestJS项目》
数据库·typescript
加号34 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏4 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐4 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再4 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip