Java Agent实现无侵入监控
Java Agent允许在JVM层面拦截和修改字节码,实现无侵入的监控,主要思路是通过字节码增强技术在方法执行前后插入监控代码。

核心实现方案
// 1. 创建Agent入口类
public class MonitorAgent {
// premain方式 - 启动时加载
public static void premain(String args, Instrumentation inst) {
System.out.println("Monitor Agent premain called");
inst.addTransformer(new MethodMonitorTransformer(), true);
}
// agentmain方式 - 运行时加载
public static void agentmain(String args, Instrumentation inst) {
System.out.println("Monitor Agent agentmain called");
inst.addTransformer(new MethodMonitorTransformer(), true);
}
}
// 2. 实现ClassFileTransformer
public class MethodMonitorTransformer implements ClassFileTransformer {
private static final Set<String> MONITORED_PACKAGES = new HashSet<>();
static {
MONITORED_PACKAGES.add("com/example/service");
MONITORED_PACKAGES.add("com/example/controller");
}
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
// 只监控指定包下的类
if (!shouldMonitor(className)) {
return null;
}
try {
return enhanceClass(className, classfileBuffer);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private boolean shouldMonitor(String className) {
return MONITORED_PACKAGES.stream()
.anyMatch(pkg -> className != null && className.startsWith(pkg));
}
private byte[] enhanceClass(String className, byte[] classfileBuffer) {
// 使用ASM进行字节码增强
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new MonitorClassVisitor(cw, className);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
}
}
ASM字节码增强实现
// 3. 方法级别的监控增强
public class MonitorMethodVisitor extends MethodVisitor {
private final String className;
private final String methodName;
private final String methodDesc;
public MonitorMethodVisitor(MethodVisitor mv, String className,
String methodName, String methodDesc) {
super(Opcodes.ASM7, mv);
this.className = className;
this.methodName = methodName;
this.methodDesc = methodDesc;
}
@Override
public void visitCode() {
// 方法开始前插入监控代码
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/example/monitor/MonitorCollector",
"start",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
false);
super.visitCode();
}
@Override
public void visitInsn(int opcode) {
// 在return指令前插入监控代码
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
|| opcode == Opcodes.ATHROW) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/example/monitor/MonitorCollector",
"end",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
false);
}
super.visitInsn(opcode);
}
}
// 4. 监控数据收集器
public class MonitorCollector {
private static final Map<String, MethodMetrics> metricsMap = new ConcurrentHashMap<>();
private static final ThreadLocal<Long> startTime = new ThreadLocal<>();
public static void start(String className, String methodName, String desc) {
startTime.set(System.currentTimeMillis());
}
public static void end(String className, String methodName, String desc) {
Long start = startTime.get();
if (start == null) return;
long duration = System.currentTimeMillis() - start;
String key = className + "#" + methodName;
metricsMap.compute(key, (k, v) -> {
if (v == null) {
return new MethodMetrics(duration, 1);
}
v.addDuration(duration);
v.incrementCount();
return v;
});
// 发送到监控系统
reportToMonitorSystem(className, methodName, duration);
}
// 获取监控数据
public static Map<String, MethodMetrics> getMetrics() {
return new HashMap<>(metricsMap);
}
}
META-INF配置
在META-INF/MANIFEST.MF中配置:
Manifest-Version: 1.0 Premain-Class: com.example.monitor.MonitorAgent Agent-Class: com.example.monitor.MonitorAgent Can-Retransform-Classes: true Can-Redefine-Classes: true
使用方式
# 启动时加载
java -javaagent:monitor-agent.jar -jar application.jar
# 运行时加载(需要Attach API)
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent("monitor-agent.jar");
vm.detach();
高级功能扩展
// 5. 性能监控增强 - 统计P99延迟
public class PercentileMetrics {
private final ConcurrentSkipListMap<Long, Integer> latencyMap = new ConcurrentSkipListMap<>();
public long getLatencyAtPercentile(double percentile) {
long total = getTotalCount();
long target = (long) (total * percentile / 100);
long cumulative = 0;
for (Map.Entry<Long, Integer> entry : latencyMap.entrySet()) {
cumulative += entry.getValue();
if (cumulative >= target) {
return entry.getKey();
}
}
return 0;
}
}
// 6. 异常监控
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExceptionMonitor {
Class<? extends Throwable>[] value() default {Exception.class};
}
// 增强时检查注解
public class ExceptionMonitorAdvisor {
public static void addExceptionMonitoring(ClassNode cn, MethodNode mn) {
if (mn.visibleAnnotations != null) {
for (AnnotationNode an : mn.visibleAnnotations) {
if (an.desc.contains("ExceptionMonitor")) {
// 在方法开始和异常处理中添加监控
}
}
}
}
}
最佳实践建议
-
性能优化:
- 使用
COMPUTE_MAXS而非COMPUTE_FRAMES减少计算开销 - 在
transform方法中尽早过滤不需要增强的类 - 使用ThreadLocal减少锁竞争
- 使用
-
稳定性保障:
- 添加增强失败的回退机制
- 限制最大重试次数防止递归
- 监控Agent本身的资源消耗
-
安全考虑:
- 对增强的类进行白名单过滤
- 避免增强核心JDK类
- 提供安全降级机制
这种方案可以做到业务代码零侵入,并且支持动态加载和卸载,非常适合生产环境的监控需求。