在做这个项目之前,我对“无侵入监控”的理解其实很朴素:
不改业务代码、不影响性能、把数据收上来就行。
但当我真正开始写一个 Java 方法级监控探针之后,才发现这四个字背后,全是工程取舍。
这篇文章不是教程,也不是“如何实现一个 Agent”,而是一次真实的工程复盘:
我在做 Pulse 这个 Java 监控探针时,踩过哪些坑,又为什么最后选择了现在这套设计。
一、为什么不是直接用现成方案?
说实话,一开始我也没打算“自己造轮子”。
APM、SkyWalking、Pinpoint、Micrometer,这些我都用过,也都很成熟。但在真实项目中,我反复遇到几个问题:
- 只想监控少量关键方法,但接入成本偏高
- 想做方法级埋点,但不想侵入业务代码
- 希望监控逻辑可控、可裁剪,而不是整套 APM
这时候我才意识到:
我需要的不是一个完整 APM,而是一个足够轻、足够“听话”的探针。
于是,Pulse 这个项目诞生了。
二、“无侵入”的第一道坎:Agent 到底侵不侵入?
从技术上讲,Java Agent 确实不改源码,但它并不等于“零成本”。
真正的问题在于:
- 字节码增强是否影响启动时间?
- 方法拦截是否引入额外耗时?
- 出现异常时,会不会反向影响业务线程?
我给自己定了一个硬约束:
探针的任何异常,都不允许影响业务逻辑。
这直接影响了后续几乎所有设计决策。
三、为什么我没有把上报逻辑写死?
在最早的版本里,我犯过一个“看起来很自然”的错误:
把 HTTP 上报逻辑直接写进探针里。
结果很快就暴露问题:
- 网络抖动 → 探针逻辑复杂化
- 想换 Kafka / Log → 要改 Agent
- 想只本地落盘 → 需要大改代码
这本质上是强耦合。
后来我彻底推翻了这个方案,引入了一个很简单但很关键的设计:
Reporter SPI。
四、Agent 只做一件事:采集事件
在现在的设计里,Pulse 的 Agent 只负责三件事:
- 拦截被指定注解的方法
- 采集执行耗时、异常、基本上下文
- 生成一个标准的
MonitorEvent
至于事件怎么处理?
- HTTP 上报
- Kafka 投递
- 打日志
- 本地文件
全部交给 Reporter。
Agent 和 Reporter 之间只通过一个 SPI 接口通信。
这带来的直接好处是:
- Agent 极度稳定
- 上报方式可以无限扩展
- 失败时可以快速降级
五、性能问题,比“能不能用”更重要
很多人问我最多的一个问题是:
Java Agent 会不会拖慢系统?
我的回答是:
会,但可以控制在一个可接受范围内。
Pulse 在设计时刻意做了几件事:
- 事件写入异步队列
- 批量上报而非逐条发送
- 可配置采样与字段采集
- 所有异常吞掉,不反抛
在真实压测中,方法级监控对主线程的影响基本可以控制在 毫秒级以内,而且是可观测、可调整的。
六、为什么我选择先开源?
我很清楚,这类项目要想商业化,难度不小。
但我依然选择先开源,原因很现实:
- 验证设计是否站得住
- 接受真实使用场景的反馈
- 把“监控探针”这件事拆得足够清楚
至少目前,Pulse 更像是一个工程型开源项目,而不是产品。
七、一些还没做,但刻意不做的事情
为了保持项目的边界清晰,有些功能我明确选择不做:
- 不做完整链路追踪
- 不做复杂指标分析
- 不强制采集入参 / 返回值
这些都不是技术做不到,而是不符合这个项目的定位。
最后
写这个项目的过程,其实让我对“无侵入”“轻量”“可扩展”这些词,有了更具体的理解。
如果你也在做类似的事情,或者正在评估 Java Agent / 监控方案,欢迎一起交流。
项目目前已经开源,如果你有兴趣,可以看看代码结构,或者直接提 Issue 讨论设计取舍。
Pulse(Java 无侵入方法级监控探针)
👉 后端: gitee.com/hanhongweis…
👉 前端: gitee.com/hanhongweis…
后面我也会继续写一些更偏设计与性能的内容。 下面是一些项目的截图