4、Mock回放方案架构设计:总体架构、组件划分与数据流

架构设计这事儿,说白了就是定规矩。你要先画好蓝图,再决定谁干什么活,数据怎么流转。我个人习惯,做Mock回放方案时,先考虑三个问题:数据从哪来?存哪去?怎么比对?想清楚这些,架构自然就出来了。

嗯,这里要注意,架构不是越复杂越好。我见过不少团队,一上来就搞微服务、消息队列、分布式存储,结果项目拖了半年还没上线。其实Mock回放的架构,最重要的是清晰和稳定。下面我就把这套经过实战检验的架构讲给你听。

4.1 总体架构:三层两通道模型

这是我比较喜欢的一种划分方式。把整个系统切分成三层,外加两条数据通道。三层分别是:控制层、数据层、执行层。两条通道是:录制通道和回放通道。

核心思路:录制时,把真实请求和响应完整抓下来;回放时,用录好的数据代替真实服务。这个过程要透明、可重复、可追溯。

控制层负责调度和配置。比如,你要给哪个接口开录制?录制多久?回放时用哪个版本的数据?这些都由控制层决定。数据层负责存储和管理录制下来的会话。执行层则负责实际拦截请求、注入Mock数据。

你想想看,如果这三层职责不清,会出什么问题?我以前就遇到过,执行层把数据存到了本地文件,结果控制层找不到数据,回放直接失败。所以,职责边界一定要画清楚。

Mock回放方案总体架构 控制层(Control Layer) 配置管理 | 调度策略 | 版本控制 | 规则引擎 负责全局配置、任务调度、会话版本管理 数据层(Data Layer) 会话存储 请求/响应/元数据 索引服务 快速检索/匹配 会话对比引擎 差异比对/报告 负责录制数据的存储、检索、比对和版本管理 执行层(Execution Layer) 请求拦截器 HTTP/gRPC/DB 数据匹配器 模糊/精确匹配 响应注入器 Mock数据回放 健康检查 存活/可用性 下发配置 上传会话 录制通道:执行层 → 数据层;回放通道:控制层 → 数据层 → 执行层

4.2 组件划分:各司其职

架构分好了,接下来就是具体组件。我习惯把组件按职责拆成6个核心模块。每个模块都只做一件事,但要把这件事做到极致。

组件名称 核心职责 常见实现方式
Mock控制引擎 全局调度、配置下发、策略执行 独立服务 + 定时任务
Mock管理服务 会话存储、索引、版本控制 MySQL + Redis + MinIO
会话持久化组件 录制数据写入、回放数据读取 H2数据库 / 本地文件
请求拦截器 拦截目标接口请求,收集请求上下文 AOP / Agent / 代理
回放校验器 比对录制数据与回放数据的差异 规则引擎 + 自定义脚本
健康监控组件 检查Mock服务可用性、数据完整性 Prometheus + 自定义指标

我的经验:组件拆分时,别想着一步到位。我最初只分了4个组件,后来发现会话管理太混乱,才拆出独立的「会话持久化组件」。先跑起来,再优化,别在设计阶段追求完美。

4.3 数据流:录制与回放两条线

数据流是架构的灵魂。说白了,搞懂数据从哪来到哪去,架构就成功了一半。Mock回放的数据流,其实就两条线:录制阶段和回放阶段。

4.3.1 录制阶段数据流

录制时,执行层拦截到真实请求,把请求和响应打包成会话,上传到数据层。流程大概是这样的:

  1. 请求到达:请求拦截器捕获到外部请求。
  2. 上下文收集:提取请求头、参数、Body、响应状态码、耗时。
  3. 会话打包:将所有信息封装成一个会话对象,带上时间戳和唯一ID。
  4. 存储入库:通过Mock管理服务写入存储层。
  5. 索引建立:根据接口名、参数哈希、时间范围建立索引。
// 会话数据的核心结构,我用JSON来举例
{
  "sessionId": "20240501-001",
  "timestamp": 1714521600000,
  "apiPath": "/api/v1/order/create",
  "method": "POST",
  "request": {
    "headers": {"Content-Type": "application/json"},
    "body": {"userId": 1001, "amount": 99.9}
  },
  "response": {
    "status": 200,
    "body": {"code": 0, "message": "success"}
  },
  "metadata": {
    "duration": 150,
    "source": "production-gateway"
  }
}

嗯,这里有个坑。我之前录制时,没考虑请求参数中的随机数,结果每次录制都生成新会话,存储爆炸。后来加了参数归一化处理,把动态字段提取出来单独存储。

4.3.2 回放阶段数据流

回放时,流程反过来。执行层拦截到请求,去数据层找匹配的会话,把录好的响应返回。流程如下:

  1. 请求拦截:同样由请求拦截器捕获。
  2. 匹配查询:根据请求的接口、参数哈希,去数据层检索。
  3. 会话加载:找到匹配的会话,加载响应数据。
  4. 响应注入:把录好的响应原样返回给调用方。
  5. 校验比对:回放校验器对比本次回放与录制时的差异。

曾经踩过的坑:回放时,我默认返回第一个匹配到的会话。结果有一次录了两条几乎一样的记录,回放时返回了错误的版本。后来我加了「版本号+时间范围」的精确匹配策略,才彻底解决这个问题。

4.4 数据流时序与交互

为了让你更直观理解数据流转,我画了个简化的时序图。这不是SVG,是我用文字描述的流程:

步骤 发起方 接收方 数据内容
1 请求拦截器 会话打包器 原始请求
2 会话打包器 Mock管理服务 结构化会话
3 Mock管理服务 持久化存储 序列化数据
4 回放数据匹配器 Mock管理服务 查询请求
5 Mock管理服务 响应注入器 匹配的会话
6 响应注入器 调用方 Mock响应

一个建议:数据流的设计一定要考虑异常场景。比如录制时服务挂了怎么办?回放时找不到匹配数据怎么办?我通常会在关键路径上加「降级策略」,找不到匹配的就走真实调用,至少保证业务不断。

4.5 架构设计的避坑指南

最后,我掏心窝子说几个容易踩的坑。这些都是我真实经历过的,希望你别再走一遍。

  • 数据一致性坑:录制和回放的数据格式要一致。我遇到过录制时用JSON,回放时用Protobuf,结果序列化失败。统一协议栈,或者做双层转换。
  • 状态冲突坑:如果两边都修改了同一个共享状态(比如数据库),回放时可能抛出脏数据。解决办法是做状态隔离,Mock服务单独用影子库。
  • 时间戳依赖坑:有的接口响应里带着时间戳,回放时比对不过。我一般会做时间戳归一化,把时间戳替换成相对时间。
  • 资源开销坑:录制全量数据会爆炸。我记得有个项目,一小时就录了20GB数据。后来加了采样率和TTL策略,问题才解决。

我的总结:架构设计没有银弹。你能做的就是:先把大框架搭好,组件职责划清,数据流走通,然后根据实际业务不断调整。Mock回放的架构,核心就一句话——录制时收得干净,回放时发得精确,比对时看得明白。