4、Mock回放方案架构设计:总体架构、组件划分与数据流
架构设计这事儿,说白了就是定规矩。你要先画好蓝图,再决定谁干什么活,数据怎么流转。我个人习惯,做Mock回放方案时,先考虑三个问题:数据从哪来?存哪去?怎么比对?想清楚这些,架构自然就出来了。
嗯,这里要注意,架构不是越复杂越好。我见过不少团队,一上来就搞微服务、消息队列、分布式存储,结果项目拖了半年还没上线。其实Mock回放的架构,最重要的是清晰和稳定。下面我就把这套经过实战检验的架构讲给你听。
4.1 总体架构:三层两通道模型
这是我比较喜欢的一种划分方式。把整个系统切分成三层,外加两条数据通道。三层分别是:控制层、数据层、执行层。两条通道是:录制通道和回放通道。
核心思路:录制时,把真实请求和响应完整抓下来;回放时,用录好的数据代替真实服务。这个过程要透明、可重复、可追溯。
控制层负责调度和配置。比如,你要给哪个接口开录制?录制多久?回放时用哪个版本的数据?这些都由控制层决定。数据层负责存储和管理录制下来的会话。执行层则负责实际拦截请求、注入Mock数据。
你想想看,如果这三层职责不清,会出什么问题?我以前就遇到过,执行层把数据存到了本地文件,结果控制层找不到数据,回放直接失败。所以,职责边界一定要画清楚。
4.2 组件划分:各司其职
架构分好了,接下来就是具体组件。我习惯把组件按职责拆成6个核心模块。每个模块都只做一件事,但要把这件事做到极致。
| 组件名称 | 核心职责 | 常见实现方式 |
|---|---|---|
| Mock控制引擎 | 全局调度、配置下发、策略执行 | 独立服务 + 定时任务 |
| Mock管理服务 | 会话存储、索引、版本控制 | MySQL + Redis + MinIO |
| 会话持久化组件 | 录制数据写入、回放数据读取 | H2数据库 / 本地文件 |
| 请求拦截器 | 拦截目标接口请求,收集请求上下文 | AOP / Agent / 代理 |
| 回放校验器 | 比对录制数据与回放数据的差异 | 规则引擎 + 自定义脚本 |
| 健康监控组件 | 检查Mock服务可用性、数据完整性 | Prometheus + 自定义指标 |
我的经验:组件拆分时,别想着一步到位。我最初只分了4个组件,后来发现会话管理太混乱,才拆出独立的「会话持久化组件」。先跑起来,再优化,别在设计阶段追求完美。
4.3 数据流:录制与回放两条线
数据流是架构的灵魂。说白了,搞懂数据从哪来到哪去,架构就成功了一半。Mock回放的数据流,其实就两条线:录制阶段和回放阶段。
4.3.1 录制阶段数据流
录制时,执行层拦截到真实请求,把请求和响应打包成会话,上传到数据层。流程大概是这样的:
- 请求到达:请求拦截器捕获到外部请求。
- 上下文收集:提取请求头、参数、Body、响应状态码、耗时。
- 会话打包:将所有信息封装成一个会话对象,带上时间戳和唯一ID。
- 存储入库:通过Mock管理服务写入存储层。
- 索引建立:根据接口名、参数哈希、时间范围建立索引。
// 会话数据的核心结构,我用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 回放阶段数据流
回放时,流程反过来。执行层拦截到请求,去数据层找匹配的会话,把录好的响应返回。流程如下:
- 请求拦截:同样由请求拦截器捕获。
- 匹配查询:根据请求的接口、参数哈希,去数据层检索。
- 会话加载:找到匹配的会话,加载响应数据。
- 响应注入:把录好的响应原样返回给调用方。
- 校验比对:回放校验器对比本次回放与录制时的差异。
曾经踩过的坑:回放时,我默认返回第一个匹配到的会话。结果有一次录了两条几乎一样的记录,回放时返回了错误的版本。后来我加了「版本号+时间范围」的精确匹配策略,才彻底解决这个问题。
4.4 数据流时序与交互
为了让你更直观理解数据流转,我画了个简化的时序图。这不是SVG,是我用文字描述的流程:
| 步骤 | 发起方 | 接收方 | 数据内容 |
|---|---|---|---|
| 1 | 请求拦截器 | 会话打包器 | 原始请求 |
| 2 | 会话打包器 | Mock管理服务 | 结构化会话 |
| 3 | Mock管理服务 | 持久化存储 | 序列化数据 |
| 4 | 回放数据匹配器 | Mock管理服务 | 查询请求 |
| 5 | Mock管理服务 | 响应注入器 | 匹配的会话 |
| 6 | 响应注入器 | 调用方 | Mock响应 |
一个建议:数据流的设计一定要考虑异常场景。比如录制时服务挂了怎么办?回放时找不到匹配数据怎么办?我通常会在关键路径上加「降级策略」,找不到匹配的就走真实调用,至少保证业务不断。
4.5 架构设计的避坑指南
最后,我掏心窝子说几个容易踩的坑。这些都是我真实经历过的,希望你别再走一遍。
- 数据一致性坑:录制和回放的数据格式要一致。我遇到过录制时用JSON,回放时用Protobuf,结果序列化失败。统一协议栈,或者做双层转换。
- 状态冲突坑:如果两边都修改了同一个共享状态(比如数据库),回放时可能抛出脏数据。解决办法是做状态隔离,Mock服务单独用影子库。
- 时间戳依赖坑:有的接口响应里带着时间戳,回放时比对不过。我一般会做时间戳归一化,把时间戳替换成相对时间。
- 资源开销坑:录制全量数据会爆炸。我记得有个项目,一小时就录了20GB数据。后来加了采样率和TTL策略,问题才解决。
我的总结:架构设计没有银弹。你能做的就是:先把大框架搭好,组件职责划清,数据流走通,然后根据实际业务不断调整。Mock回放的架构,核心就一句话——录制时收得干净,回放时发得精确,比对时看得明白。