步骤一:先抓正常样本,建立对照
测试场景设为:用户点击一次“提交订单”,偶发生成两笔订单。先清空 Charles 会话,只保留订单域名,关闭 Rewrite 和断点,避免工具本身改变请求。正常操作一次,记录请求方法、路径、开始时间、耗时、状态码和请求体。
假设接口为 POST /orders,正常样本只有一条,请求体包含商品与地址,服务端返回订单号。这里不能只截一张列表图,还要保存会话;后续要展开请求头,核对是否存在请求 ID、幂等键或客户端生成的业务流水号。
查尔斯对比真正有价值的地方,是把“感觉请求发了两次”变成可核对的时间线。下面用一个可复现的移动端重复提交案例,还原从抓取基线、比较两条请求,到弱网复现和验证修复的完整流程。重点不是某个按钮,而是如何让网络证据指向责任环节。
测试场景设为:用户点击一次“提交订单”,偶发生成两笔订单。先清空 Charles 会话,只保留订单域名,关闭 Rewrite 和断点,避免工具本身改变请求。正常操作一次,记录请求方法、路径、开始时间、耗时、状态码和请求体。
假设接口为 POST /orders,正常样本只有一条,请求体包含商品与地址,服务端返回订单号。这里不能只截一张列表图,还要保存会话;后续要展开请求头,核对是否存在请求 ID、幂等键或客户端生成的业务流水号。
开启 Charles 的 Throttle,选择接近高延迟、低带宽的参数,再点击一次提交。若列表中出现两条 POST /orders,按 Sequence 查看先后关系:第二条是在第一条尚未返回时发出,还是收到超时后才发出,两种情况对应不同代码路径。
继续对比请求体和请求头。若两条请求内容相同、时间相隔数百毫秒,却使用不同流水号,常见方向是按钮未禁用或事件重复绑定;若间隔接近客户端超时阈值,且第二条带有重试标记,则应检查自动重试策略。
Charles 只能证明客户端侧观察到什么,不能单独证明服务端最终处理了几次。用请求 ID、用户 ID和时间窗口去查网关与订单服务日志。如果 Charles 有两条请求、网关也收到两条,问题在客户端重发或中间层重试;若网关只有一条,则要检查代理环境和抓取方式。
反过来,Charles 只有一条而服务端创建两笔订单,更应追查服务内部重复消费、事务边界或消息队列。这个查尔斯对比动作能避免前端、网关、后端互相猜测,因为每一段链路都有可对齐的标识。
合理修复通常有两道:客户端提交后禁用按钮并控制重试,服务端按幂等键保证同一业务请求只落一单。修复完成后,保持相同弱网参数连续测试,确认即使出现两次传输,服务端也返回同一个业务结果,而不是再创建订单。
最后关闭 Throttle,再做正常网络回归,并导出脱敏后的会话作为缺陷附件。删除令牌、手机号和地址后再共享。一次有效复盘,应留下基线、异常样本、日志对齐结果和回归证据,而不是一句“Charles 抓到两次”。
比较两次请求的时间间隔、请求 ID、幂等键、重试标记和第一条请求的返回时机,再与客户端超时配置对齐。仅凭路径相同不能下结论。
不能完全代替。Throttle 适合稳定复现延迟和带宽限制,但真实网络还有抖动、切网、丢包和基站差异,关键流程仍需真机实网验证。
先脱敏。会话可能包含 Cookie、Authorization、手机号、地址和响应数据,应删除无关请求或使用测试账号重新采集。