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

结束

不知道写哈了,结束吧

相关推荐
你不是我我23 分钟前
【Java 开发日记】我们来说一说 Redis 主从复制的原理及作用
java·redis·github
程序员葫芦娃23 分钟前
【Java毕设项目】基于SSM的旅游资源网站
java·开发语言·数据库·编程·课程设计·旅游·毕设
Yuer202525 分钟前
用 Rust 做分布式查询引擎之前,我先写了一个最小执行 POC
开发语言·分布式·rust
2401_865854881 小时前
怎样挑选适合业务的数据库云服务?
数据库
lkbhua莱克瓦241 小时前
基础-函数
开发语言·数据库·笔记·sql·mysql·函数
福大大架构师每日一题1 小时前
dify 1.11.2 正式发布:向量数据库、安全增强、测试优化与多语言支持全面升级
数据库·安全
码农学院2 小时前
Mysql 中的性能调优方法
数据库·mysql
UrSpecial2 小时前
MySQL索引
数据库·mysql
DB虚空行者2 小时前
聊下几次线上删除MySQL导致的故障
数据库
张彦峰ZYF2 小时前
高并发场景下的缓存雪崩探析与应对策略
redis·分布式·缓存