pfinder实现原理揭秘
内容提要
本文介绍了京东自研的pfinder应用全链路监控系统的核心原理和架构设计,包括多维监控、自动埋点、应用拓扑、调用链追踪等功能。pfinder使用字节码注入实现增强,解决了跨线程传递traceId的问题。
延伸解读
字节码增强框架选型:性能与易用性的权衡
pfinder 的核心是字节码增强,而实现增强有多种框架可选。文章对比了 ASM、Javassist、ByteBuddy 和 ByteKit:ASM 性能最高但需精通字节码,学习成本高;Javassist 易用性较好但性能稍逊;ByteBuddy 和 ByteKit 在易用性和性能之间取得平衡,且支持断点调试。ByteKit 还能防止重复增强。选型时需根据团队技术储备和性能要求权衡。
JVMTI 与 Instrumentation:Java Agent 的基石
pfinder 的字节码增强依赖于 JVM 提供的底层接口。JVMTI 是 JVM 工具接口,允许监控 JVM 运行时指标和调试,但需用 C 语言开发。JDK5 后提供的 Instrumentation 接口让开发者能用 Java 编写 Agent,它基于 JVMTI 实现。通过 Instrumentation 的 addTransformer 等方法,可以修改类字节码,实现增强。pfinder 正是利用这些机制在启动时加载插件并完成增强。
跨线程 traceId 传递:TracingRunnable 的包装机制
在异步场景中,MDC 基于 ThreadLocal 保存的 traceId 会丢失。pfinder 通过字节码增强解决了这一问题。以线程池执行 Runnable 为例,pfinder 用 TracingRunnable 包装原始任务,在构造函数中将主线程的 traceId 通过 snapshot 参数传递,在 run 方法中将 snapshot 放入上下文,再取出放到子线程的 MDC 中,从而实现 traceId 跨线程传递。
在线热部署的探索与局限
基于 javaagent 和 JMTP 命令交互,pfinder 支持类搜索、反编译和热更新等在线热部署功能。但文章也指出其局限性:Instrumentation 出于安全考虑不允许修改类结构,如新增字段、方法或修改父类。要突破此限制,需使用 Dcevm 等补丁。此外,对 Spring XML、MyBatis XML 的支持也有待完善。这些是实际应用中需要注意的边界。
Q&A
pfinder是什么,它的主要功能有哪些?
pfinder是京东自研的APM系统,主要功能包括调用链追踪、应用拓扑、多维监控、自动埋点和自动故障分析等。
pfinder如何实现字节码增强?
pfinder通过字节码注入技术实现增强,使用多种字节码修改框架如ASM、javassist等,解决了跨线程传递traceId的问题。
pfinder的接入方式是什么?
pfinder无需修改代码,只需在启动文件中增加两行脚本即可接入。
pfinder支持哪些中间件和开源组件?
pfinder支持京东主流中间件如jimdb、jmq、jsf,以及常用开源组件如tomcat、http client、mysql和es等。
pfinder如何解决跨线程传递traceId的问题?
pfinder通过TracingRunnable实现traceId的跨线程传递,确保在使用线程池或@Async时不会丢失traceId。
pfinder的在线热部署功能是如何实现的?
pfinder通过jmtp与服务端交互,允许类搜索、反编译和热更新等功能,从而实现在线热部署。