基于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操作,可尝试使用事务方式执行。

结束

不知道写哈了,结束吧

相关推荐
ClouGence1 天前
Oracle CDC 架构优化:从主库直连到 DataGuard 备库同步
数据库·后端·oracle
无响应de神1 天前
三、用户与权限管理
数据库·mysql
小小工匠2 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
麦聪聊数据2 天前
数据服务化时代:企业数据能力输出的核心路径
数据库
shushangyun_2 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
DARLING Zero two♡2 天前
【MySQL数据库】数据类型与表约束
数据库·mysql
曹牧2 天前
Oracle EXPLAIN PLAN
数据库·oracle
BD_Marathon2 天前
SQL学习指南——视图
数据库·sql
活宝小娜2 天前
mysql详细安装教程
数据库·mysql·adb
贤时间2 天前
codex 助力oracle ebs 开发
数据库·oracle