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

结束

不知道写哈了,结束吧

相关推荐
程序边界1 天前
深度体验金仓时序数据库:从“存储与分析困局“到低成本落地的实战记录
数据库·时序数据库
茁壮成长的露露1 天前
MongoDB副本集
数据库·mongodb
想摆烂的不会研究的研究生1 天前
MySQL海量数据深分页优化
数据库·redis·后端·mysql·缓存
廋到被风吹走1 天前
【数据库】【Mysql】MySQL 索引优化深度解析:从原理到实战
数据库·mysql·adb
霖霖总总1 天前
[小技巧32]深入理解 MySQL中的双1参数:innodb_flush_log_at_trx_commit 与 sync_binlog
运维·数据库·mysql
RockHopper20251 天前
通用工业 AMR 的分布式状态控制系统设计原理
分布式·智能制造·具身智能·amr
资深web全栈开发1 天前
实现幂等性的常用方式
分布式·幂等
枫叶丹41 天前
国产数据库新标杆:金仓以多模融合重构文档数据库未来
开发语言·数据库·重构
m0_748252381 天前
Servlet 数据库访问
数据库·servlet
liux35281 天前
MySQL事务机制深度解析:从理论到实践(五)
数据库·mysql