tt命令本质是方法快照与回放,仅针对某次调用记录参数、返回值、异常及调用栈,支持重放纯业务方法,不适用于controller层或含容器依赖的方法。

tt 命令本质是「方法快照 + 回放」,不是重放整个 HTTP 请求
Arthas 的 tt 命令不会帮你还原一次完整的 Web 请求(比如 Spring MVC 的 DispatcherServlet 调用链),它只对指定方法的**某次调用**做参数、返回值、异常、调用栈的快照,并支持用这些快照数据重新触发该方法调用。所以排查逻辑 Bug 的前提是:你得先定位到出问题的那个具体方法(比如 OrderService.calculatePrice()),而不是试图“回放一个 URL”。
常见误操作是直接对 Controller 层方法用 tt,结果发现回放失败——因为 Controller 方法依赖 HttpServletRequest、ModelAndView 等容器对象,而 tt -p 回放时这些对象并不存在或无法序列化。
- ✅ 推荐目标:纯业务方法(无外部容器依赖、无静态状态、参数可序列化)
- ❌ 避免目标:
Controller方法、含ThreadLocal逻辑的方法、调用FeignClient或数据库连接的方法(除非你 mock 掉) - ⚠️ 注意:
tt快照不捕获方法内部修改的局部变量或对象字段,只记录入参、返回值和异常
如何精准抓取一次“有问题”的方法调用
不能靠猜,得结合条件过滤。比如你想抓 PaymentService.refund() 中 amount 时的调用,就用表达式过滤:
tt -t -n 5 com.example.service.PaymentService refund 'params[0] <p>说明:</p>
-
-t表示开启 trace 模式(记录调用时间、耗时、参数等) -
-n 5最多记录 5 次匹配调用,防内存溢出 'params[0] 是 OGNL 表达式,<code>params是参数数组,按顺序索引;支持params[0].getOrderId()这类链式调用- 如果方法重载,建议加
-E启用正则匹配类名/方法名,避免误抓
抓完后用 tt -l 查看所有快照,重点关注 index、cost(ms) 和 is-exception 列,找到你要复现的那条记录。
回放前必须检查三件事
tt -p 回放看似简单,但失败率极高,多数卡在这三步:
-
参数类型是否可反序列化:如果原调用传了
new HashMap() { { put("k", "v"); } }这种匿名子类,tt无法还原,回放会抛ClassNotFoundException或InvalidClassException -
目标方法是否 static / 构造器:目前
tt -p不支持回放static方法(Arthas 4.0+ 仍不支持),也不支持构造器;若误选,提示Unsupported operation: replay static method -
类加载器是否一致:快照是在某个 ClassLoader 下采集的,回放时若该类被热更新过(比如通过
redefine),可能因字节码不匹配导致VerifyError
安全做法是:回放前先 tt -i <index></index> 查看快照详情,确认 method 字段非 static,且 parameterTypes 里全是基础类型或你工程中稳定存在的 POJO 类。
回放时怎么传参、改参、验证结果
最常用的是 tt -p -i <index></index>,但它默认用原始参数原样重放。真要调试,得配合参数替换:
tt -p -i 1001 -w 'params[0].setAmount(99.9)'
说明:
-
-w后跟 OGNL 表达式,在回放前修改快照中的参数对象(注意:只改引用对象的字段,不新建对象) - 想换整个参数?用
-w 'params = { @java.math.BigDecimal@valueOf(123.45) }'(需确保类型匹配) - 回放后输出包含
return或throw,但不会自动打印日志;如果方法里有log.debug(),得提前用logger命令打开对应 level - 性能提示:回放不走 JIT 编译路径,耗时比真实调用高,别拿它测性能
复杂场景下,建议把关键快照导出为 JSON:tt -i 1001 -h > snapshot.json,再写个本地单元测试加载数据做断点调试——这比在生产上反复 tt -p 更可控。
真正难的不是命令怎么敲,而是判断哪一层方法值得快照、哪些副作用必须手动隔离、以及怎么把“线上一次偶然的错”转化成可稳定复现的输入。快照只是切片,逻辑闭环还得靠你对代码路径的理解。










