一次价值百万的Bug追踪,我们如何用Charles找回丢失的请求

37 阅读4分钟

“为什么订单没有生成?用户的钱已经扣了!”

那天晚上,我们整个项目组被紧急召回。 服务器日志正常、数据库正常、接口返回正常—— 但订单,就是凭空消失了。


一、事故发生:无法复现的“幽灵订单”

这是一家大型电商项目的促销夜,后端日志显示交易成功,支付平台也确认扣款成功,但部分用户反馈:

“我付了钱,没订单。”

这不是小Bug,每一分钟可能意味着成百上千的损失。

我们一度以为是数据库写入延迟,可所有监控都正常。

“这就像一个信号在路上被吞掉了。” 后端负责人皱着眉说。


二、排查开始:看似完美的接口

我们用Postman反复测试接口,所有请求都返回 200 OK。 日志中每次调用都显示“订单创建成功”。

但奇怪的是:用户实际下单的那批请求,后台完全没有记录

那意味着——这些请求,根本没到达服务器。


三、关键线索:Charles登场

前端工程师灵光一闪:

“那我们能不能看看,用户那边到底发了什么?”

于是他在自己的电脑上复现支付流程,并打开 Charles抓包工具 监控全程。

第一轮抓包没发现问题——所有请求都完整发送。 第二轮,他在页面反复操作,突然看到一条异常请求:

POST /api/order/create
Status: 0
Error: Connection reset by peer

这一行让所有人都安静了。


四、进一步分析:网络被谁“掐断”了?

我们在Charles中展开这条请求:

  • Header完整;
  • Body正常;
  • 响应为空

前端以为是网络卡顿,但Charles的 Timing图 告诉我们: 请求在“Sending”阶段耗时极长,几乎超时后被客户端主动断开。

于是我们在 Throttle功能 下模拟了用户的网络状况—— 3G / High Latency 模式下,同样的请求再次中断。

真相开始浮出水面:

弱网下,前端支付成功后立即跳转页面, 导致订单请求被提前取消。


五、验证假设:重放与断点调试

为了验证这个推测,我们使用了Charles的两大功能:

1. Repeat(请求重放)

将那条失败的请求重新发送,结果成功生成订单。

2. Breakpoints(断点调试)

在请求发送前设置断点,手动延迟3秒后再继续, 结果仍然成功。

这印证了假设: 问题并非接口或后端逻辑,而是前端提前切断连接


六、问题根因:过早的页面跳转

最终我们找到关键代码:

axios.post('/api/order/create', params);
window.location.href = '/success';

请求尚未完成,就跳转了页面。 在良好的网络下,一切正常; 但在高延迟环境中,请求被浏览器中断。


七、解决方案与复盘

我们修改代码为异步等待:

await axios.post('/api/order/create', params);
window.location.href = '/success';

并在弱网环境下通过Charles Throttle测试, 验证多次都正常。

随后,我们将该问题归纳为:

环节问题Charles功能作用
前端请求异常页面跳转中断请求SSL Proxying + Timeline分析请求状态
弱网模拟复现场景Throttle复现问题条件
请求重试验证确认可复现Repeat验证假设
调试验证检查响应延迟Breakpoints控制请求时机

八、这次事故的代价与收获

那次促销活动,我们因为Bug造成了近百万订单补偿。 但也正因为这次危机,我们重新审视了调试体系的重要性。

Charles成为团队的“标配工具”:

  • 前端用于监控接口调用;
  • 测试用它复现问题;
  • 后端用它分析请求状态。

我们甚至在内部wiki中写下了这句话:

“调试不靠猜,先抓包再开口。”


推荐学习资源

如果你也想系统学习如何用Charles做问题排查、性能分析、移动端抓包,可以访问 Charles中文网

这里有:

  • 安装与配置教程
  • HTTPS证书操作指南
  • Rewrite与Throttle高级功能讲解
  • 多端实战案例与排查思路

一次Bug的代价,是一次团队的成长

那次事故之后,我们再也不敢“盲调”。 每当遇到接口异常,团队第一反应就是:“开Charles。”

因为我们知道,在那片看似混乱的流量里,总能找到真相。