1. 内联插桩
agent 注册一个 ClassFileTransformer;每个类被加载时,JVM 把原始字节码递给它,
ByteBuddy 用 @Advice 把「进入记时间 / 退出算耗时」几行指令原地拷贝进方法体。
不生成代理类、不改方法签名。
Go/Node 把程序编译成「罐头」,Java 把程序养在「培养舱」里。这个页面就是培养舱的观察窗: 一个核心插桩不足百行的 Java Agent(ByteBuddy 插桩)挂在正在服务这个页面的后端进程上, 把方法调用(耗时 ≥ 10µs)的耗时、线程、异常记下来——业务代码一行都没改。
实时采集 · 每 2 秒拉取增量这些数字来自 agent 自身的 /agent/stats 接口——它甚至统计了自己过滤掉了多少噪声调用。
每条泳道是一条线程,横条是一次方法调用(位置 = 发生时刻,宽度 = 耗时)。 反应式服务里线程名信息量爆炸:蓝色是 Netty 事件循环(请求响应的主战场),橙色是 boundedElastic(兜住阻塞操作)。
每个事件都带完整坐标:类、方法、耗时、线程、嵌套深度。这就是 APM 链路追踪的原始形态。
| 时刻 | 方法 | 耗时 | 线程 | 深度 |
|---|---|---|---|---|
| 暂无数据 | ||||
全部魔法集中在类加载的一瞬间,磁盘上的 jar 和源码仓库自始至终没有被打开过。
agent 注册一个 ClassFileTransformer;每个类被加载时,JVM 把原始字节码递给它,
ByteBuddy 用 @Advice 把「进入记时间 / 退出算耗时」几行指令原地拷贝进方法体。
不生成代理类、不改方法签名。
ByteBuddy 自身被改名打包进 agent jar(shaded.bytebuddy),
与业务 classpath 上任何版本的 byte-buddy 物理隔离——这是所有生产级 agent(OTel、Arthas)的标准姿势。
事件写进进程内的环形缓冲,agent 自带一个只绑定 127.0.0.1 的 HTTP 接口; 对外只靠一个两个 GET 方法的转发 controller。读者把 agent 挂到任何 Spring 项目上,同样三步就能复现。
?api=http://localhost:8080(指向你本地服务),
再给你的服务挂上 trace-agent.jar(仓库 trace-agent/ 目录,./mvnw package 一条命令构建),
你自己的服务也能被这样「开天眼」。
SkyWalking / OpenTelemetry 的 javaagent 就是这个机制的产品化。