第三章:常见异常场景分类——先看地图再动手
做Mock回放故障排查,最怕什么?
怕你一头扎进日志里,翻了半天还不知道问题在哪。我个人习惯是先分类——搞清楚当前属于哪一类异常,再下手。好比修车,你先得知道是发动机响还是轮胎漏气,对吧?
这一章我们就来聊聊最常见的五类异常场景。说白了,就是我给你一张“故障地图”,标注好雷区位置。
核心观点: 把异常分到具体类别里,排查效率能翻倍。否则你就是在黑灯瞎火里找螺丝。
1. 网络异常——Mock服务不响应?先看链路
网络异常是Mock回放里的高频问题。回放时Mock服务突然超时,或者返回了奇怪的状态码,八成是网络层出了岔子。
- 典型表现: 连接超时、连接被拒绝、DNS解析失败、TLS握手失败。
- 为什么Mock里也会出网络问题? 你想想看,虽然Mock模拟了接口,但底层网络栈还是走真实socket。如果Mock服务部署在容器里,端口映射搞错了,或者防火墙把Mock端口拦了,一样会超时。
- 个人经验: 我在项目里遇到过一回,Mock服务启动正常,回放脚本跑了一百遍都OK,第一百零一遍突然挂了。查了半天,是Mock所在机器的网络命名空间被误改了。所以网络异常不要只盯着应用层,底层也要看。
排查小技巧: 在回放脚本里添加一个“快速连通性检查”——在发起Mock请求前先ping一下Mock地址,能过滤掉一多半网络问题。
2. 数据不一致——Mock数据和真实数据对不上
这个类别我遇到的最多。回放时Mock返回的响应和录制时的响应不一致,导致下游处理逻辑出错。
- 典型表现: 字段值不匹配(比如录制时time是1234567890,回放时变成'2023-01-01')、字段缺失、字段类型不同(string vs int)。
- 背后原因: Mock录制的数据是静态快照,但回放时请求携带的参数或上下文变了,导致Mock匹配规则没命中,fallback到了默认响应。
- 避坑指南: 我曾经因为录制的JSON里包含了一个会话ID,回放时没有替换掉,导致Mock一直匹配不到,返回了空响应。后来我强制在录制时把所有动态字段打上占位符(比如
${sessionId}),回放时再用实际值注入。这招很管用。
| 不一致类型 | 典型原因 | 快速定位方法 |
|---|---|---|
| 字段缺失 | 录制时接口升级,字段被删除 | 对比录制/回放响应schema |
| 字段值改变 | 动态时间戳、随机数未mock | 用diff工具对比两个JSON |
| 类型变化 | 服务端换了序列化方式 | 检查Content-Type及序列化配置 |
3. 状态冲突——Mock与外部系统状态拧巴了
这个场景比较隐蔽。Mock本身是纯模拟,但你的被测系统可能依赖某些外部状态——比如数据库里的用户余额、缓存里的、或者其他服务的会话。如果Mock的状态和真实系统状态对不上,就会出问题。
- 典型表现: 同样的Mock响应,在环境A上通过了,在环境B上失败。或者同一个TestCase,第一次跑成功,第二次跑失败。
- 核心原因: Mock只控制了接口层,但业务系统内部还有状态机。举个例子,模拟“支付成功”的Mock响应后,被测系统会去更新订单状态。如果数据库里订单当前状态是“已取消”,那更新就会报错。
- 我建议: 遇到状态冲突时,先把Mock和被测系统的“上下文”记录下来。不要只看Mock返回了什么,还要看被测系统在调用Mock之前做过什么。
4. 并发竞态——多线程下Mock响应乱了
并发场景下的Mock回放,是真正考验功力的地方。如果你的被测系统是多线程并发调用同一个Mock端点,那Mock可能会被同时命中多次。
- 典型表现: 一个请求获得了本该给另一个请求的Mock响应,或者Mock被重入导致响应错乱。
- 为什么会这样? 大多数Mock框架默认是单线程模式(比如WireMock的stub匹配是线程安全的,但响应生成如果有共享变量就不安全)。如果你在Mock响应里用了全局计数器、共享缓存,那并发时就会出问题。
- 现场案例: 我记得有一次排查一个“偶发空指针”的bug,回放10次有2次报错。最后发现Mock的response transformer里有一个共享的HashMap被多线程同时put进去,导致数据覆盖。解决方案是把共享变量改成ThreadLocal。
注意: 并发竞态类异常往往无法稳定复现。如果你在回放日志里看到“偶尔失败”的记录,第一个怀疑的就应该是并发问题。建议在Mock回放脚本中加入并发压力测试环节。
5. 配置错误——Mock跑起来了,但配置不对
这个类别看似基础,但坑最多。Mock框架的配置项繁多:匹配策略、响应延迟、代理设置、请求过滤……任何一个配错了,回放结果都可能变形。
- 典型表现: Mock返回的HTTP状态码不对(比如该返回200的返回了500)、响应头缺失、CORS跨域设置不对导致前端报错。
- 常见误区: 很多人把WireMock的
stubFor写成了stubFor(anyUrl()),结果所有请求都匹配到同一个stub上了。或者忘了关闭Proxy模式,导致回放时Mock把请求转发了出去。 - 避坑指南: 我曾经因为把Mock的
port写成了监听localhost的IPv6地址,而被测客户端用的IPv4,结果死活连不上。配置完成后一定要通过curl -v http://localhost:port验证一次,不要想当然。
// 一个错误的配置示例
// 忘了设置priority,导致通配stub覆盖了精确stub
stubFor(get(urlPathMatching("/api/.*"))
.willReturn(aResponse().withStatus(200)));
// 下面这个精确配置永远不会被匹配到,因为上面那个匹配了所有
stubFor(get(urlEqualTo("/api/user/123"))
.willReturn(aResponse().withStatus(200)));
习惯做法: 我在项目中会在Mock启动时打印出所有注册的stub映射,核对一遍再跑回放。这样可以一眼看出有没有配置冲突。
一张图看懂所有异常分类
下面这张SVG图把五类异常和它们的内因、排查方向整理在一起。你可以把它当作快速参考卡。
这五个分类就像是五个抽屉。遇到故障时,先把现象往抽屉里放。放对了,你就知道该翻哪个工具包。放错了,只会越修越乱。