第三章:常见异常场景分类——先看地图再动手

做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图把五类异常和它们的内因、排查方向整理在一起。你可以把它当作快速参考卡。

Mock回放常见异常场景分类 异常场景分类 网络异常 连接超时 DNS解析失败 防火墙拦截 端口映射错误 数据不一致 字段缺失 值不匹配 类型变化 动态字段未替 状态冲突 外部系统状态 本地缓存过期 数据库状态不匹配 上下文丢失 并发竞态 线程安全问题 共享变量覆盖 响应错乱 偶发失败 配置错误 匹配规则冲突 端口或协议配置错 Proxy模式意外启用 实际排查中,一个故障可能跨多个分类 建议按概率从高到低逐个验证。

这五个分类就像是五个抽屉。遇到故障时,先把现象往抽屉里放。放对了,你就知道该翻哪个工具包。放错了,只会越修越乱。