沉淀、、成长,让自己和他人都能有所收获 你开发的系统是 一套系统是否稳定运行,取决于它的运行健康度,而这包括; 那么要做到这样的事情有什么监控方案呢,这里面的做法比较多。比如; 好,那么这样非入侵的探针方式是怎么实现的呢?如何去做方法的 在字节码增强方面有三个框架; 本文主要讲解关于 一步步向你展示通过指令码来改写你的方法! 通过 这是修改之前的方法 这是修改之后的方法 从修改前到修改后,可以看到。有如下几点修改; 好!如果你有很敏锐的嗅觉,或者很多小问号。那么你是否会想到如果使用到你自己的业务中,是不是就可以做一套非入侵的监控系统了? 字节码增强的过程乍一看还是比较麻烦的,如果你没有阅读过JVM虚拟机规范等相关书籍,确实很不好理解。但是也就是这部分不那么容易理解的知识,才是你后续价值的体现。 接下来我会一步步的带着你通过字节码增强的方式,来实现我们的监控需求。最终的完整的代码,可以通过关注公众号: 以上这段代码就是 接下来是对方法的访问 基本上所有的 这是一个被测试的方法; 编写指令 这个 被初次增强后的方法; 在上面我们已经将返回内容赋值给参数,那么在 定义输出结果方法; 接下来我们使用字节码增强的方式来调用这个静态方法。 再次被增强后的方法; 如果需要抓住方法的异常信息并输出,那么就需要给原有的方法包上一层 添加 添加 这次增强后的方法; 在我们使用 定义输出异常方法; 接下来的事情就很简单了,只需要在抛出异常的指令中,把调用外部方法的内容集成进去就可以了。 现在再看字节码增强后的方法; 好!到这我们已经将这个方法彻底的通过字节码改造完成,可以非常方便的监控异常信息。对用外部输出的方法,后续可以通过 这是一个字符串转换成数字类型的方法,我们通过调用传输不同的参数进行验证。比如;数字类型字符串和非数字类型字符串。 另外这里是我们通过字节码增强的方式进行改造方法,改造后这个方法反馈给我们的仍然是字节码,所以需要使用到 测试方法; 输出结果; 最近将个人原创代码库资源整理出一份 本代码库是作者小傅哥多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、、,给个Star ✨)! 链接:https://github.com/fuzhengwei/CodeGuide/wiki
作者:小傅哥
博客:https://bugstack.cn
Wiki:https://github.com/fuzhengwei/CodeGuide/wiki一、前言
裸奔
的吗?深夜被老板 Diss调用量
、可用率
、响应时长
以及服务器性能等各项指标的一个综合值。并且在系统出现异常问题时,可以抓取整个业务方法执行链路并输出;当时的入参、出参、异常信息等等。当然还包括一些JVM、Redis、Mysql的各项性能指标,以用于快速定位并解决问题。
Google Dapper
、Zipkin
等都可以实现,他们都是基于探针技术非入侵的采用字节码增强的方式进行监控。字节码增强
?ASM
、Javassist
、ByteCode
,各有优缺点按需选择。这在我们之前的字节码编程文章里也有所提到。ASM
方式的字节码增强,接下来的案例会逐步讲解一个给方法添加 TryCatch
块,用于采集异常信息以及正常的出参结果的流程。二、系统环境
三、技术目标
ASM
字节码增强技术,使用指令码将方法修改为我们想要的效果。这部分原本需要使用 JavaAgent
技术,在工程启动加载时候进行修改字节码。这里为了将关于字节码核心内容展示出来,通过加载类名称获取字节码进行修改。public Integer strToNumber(String str) { return Integer.parseInt(str); }
public Integer strToNumber(String str) { try { Integer var2 = Integer.parseInt(str); MethodTest.point("org.itstack.test.MethodTest$Test.strToNumber", var2); return var2; } catch (Exception var3) { MethodTest.point("org.itstack.test.MethodTest$Test.strToNumber", var3); throw var3; } }
TryCatch
中,并将异常也做了输出之后升职加薪四、实现过程
bugstack虫洞栈
回复源码
获取(ASM字节码编程)。1. 搭建字节码框架
/** * 字节码增强获取新的字节码 */ private byte[] getBytes(String className) throws IOException { ClassReader cr = new ClassReader(className); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS); cr.accept(new ClassVisitor(ASM5, cw) { public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { // 方法过滤 if (!"strToNumber".equals(name)) return super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new AdviceAdapter(ASM5, mv, access, name, descriptor) { // 方法进入时修改字节码 protected void onMethodEnter() {} // 访问局部变量和操作数栈 public void visitMaxs(int maxStack, int maxLocals) {} // 方法退出时修改字节码 protected void onMethodExit(int opcode) {} }; } }, ClassReader.EXPAND_FRAMES); return cw.toByteArray(); }
ASM
用于处理字节码增强的模版代码块。首先他会分别创建 ClassReader
、ClassWriter
,用于对类的加载和写入,这里的加载方式在构造方法中也提供的比较丰富。可以通过类名、字节码或者流的方式进行处理。MethodVisitor
,基本所有使用 ASM
技术的监控系统,都会在这里来实现字节码的注入。这里面目前用到了三个方法的,如下;
onMethodEnter
方法进入时设置一些基本内容,比如当前纳秒用于后续监控方法的执行耗时。还有就是一些 Try
块的开始。visitMaxs
这个是在方法结束前,用于添加 Catch
块。到这也就可以将整个方法进行包裹起来了。onMethodExit
最后是这个方法退出时,用于 RETURN
之前,可以注入结尾的字节码加强,比如调用外部方法输出监控信息。ASM
字节码增强操作,都离不开这三个方法。下面我就一步步来用指令将方法改造。2. 获取方法返回值
public Integer strToNumber(String str) { return Integer.parseInt(str); }
onMethodExit
方法就是我们上面提到的字节码编写框架中的内容,在里面添加具体的字节码指令。@Override protected void onMethodExit(int opcode) { if ((IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW) { int nextLocal = this.nextLocal; mv.visitVarInsn(ASTORE, nextLocal); // 将栈顶引用类型值保存到局部变量indexbyte中。 mv.visitVarInsn(ALOAD, nextLocal); // 从局部变量indexbyte中装载引用类型值入栈。 } }
ARETURN
的值(ARETURN,是返回对象类型,如果是返回 int
则需要使用 IRETURN)。public Integer strToNumber(String str) { Integer var2 = Integer.parseInt(str); return var2; }
return
将参数返回。这样也就可以让我们拿到了方法出参 var2
进行输出操作。3. 输出方法返回值
return
之前,我们就可以在添加一个方法来输出方法信息和出参了。public static void point(String methodName, Object response) { System.out.println("系统监控 :: [方法名称:" + methodName + " 输出信息:" + JSON.toJSONString(response) + "]rn"); }
@Override protected void onMethodExit(int opcode) { if ((IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW) { ... mv.visitLdcInsn(className + "." + name); // 类名.方法名 mv.visitVarInsn(ALOAD, nextLocal); mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(MethodTest.class), "point", "(Ljava/lang/String;Ljava/lang/Object;)V", false); } }
INVOKESTATIC
是调用指令,后面是方法的地址、方法名、方法描述。(Ljava/lang/String;Ljava/lang/Object;)V
,表示 String
和 Object
类型的入参,V
是返回空。整体看也就是我们的方法;void point(String methodName, Object response)
public Integer strToNumber(String str) { Integer var2 = Integer.parseInt(str); point("org.itstack.test.MethodTest.strToNumber", var2); return var2; }
4. 给方法加上TryCatch
TryCatch
捕获异常。接下来我们开始完成这样的指令码操作。TryCatch
开始private Label from = new Label(), to = new Label(), target = new Label(); @Override protected void onMethodEnter() { //标志:try块开始位置 visitLabel(from); visitTryCatchBlock(from, to, target, "java/lang/Exception"); }
onMethodEnter()
中,加入 TryCatch
开始块,在部分在 ASM
中固定的模式,按照需求添加即可。TryCatch
结尾@Override public void visitMaxs(int maxStack, int maxLocals) { //标志:try块结束 mv.visitLabel(to); //标志:catch块开始位置 mv.visitLabel(target); mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"}); // 异常信息保存到局部变量 int local = newLocal(Type.LONG_TYPE); mv.visitVarInsn(ASTORE, local); // 抛出异常 mv.visitVarInsn(ALOAD, local); mv.visitInsn(ATHROW); super.visitMaxs(maxStack, maxLocals); }
visitMaxs
方法中完成 TryCatch
的结尾,包住异常请抛出。mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"});
,在指定方法操作数栈中将 TryCatch
处理完成。这里面的几个参数也可以动态拼装;局部变量、参数、栈、异常。ASTORE
,将异常信息保存到局部变量,并使用指定 ALOAD
放到操作数栈,用于抛出。ATHROW
,最后是抛出异常的指令,也就是 throw var
;public Integer strToNumber(String str) { try { Integer var2 = Integer.parseInt(str); point("org.itstack.test.MethodTest.strToNumber", var2); return var2; } catch (Exception var3) { throw var3; } }
TryCatch
中,并按照需要输出我们的信息。接下来就需要将异常信息,打印出来。5. 输出异常信息
ASM
字节码增强后,已经可以将方法拓展的非常的适合于监控了。接下来我们定义一个静态方法,用于输出异常信息;public static void point(String methodName, Throwable throwable) { System.out.println("系统监控 :: [方法名称:" + methodName + " 异常信息:" + throwable.getMessage() + "]rn"); }
@Override public void visitMaxs(int maxStack, int maxLocals) { ... // 输出信息 mv.visitLdcInsn(className + "." + name); // 类名.方法名 mv.visitVarInsn(ALOAD, local); mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(MethodTest.class), "point", "(Ljava/lang/String;Ljava/lang/Throwable;)V", false); ... }
mv.visitLdcInsn
,加载常量。也就是类名和方法名。ALOAD
,将异常信息加载到操作数栈用,用于输出。INVOKESTATIC
,调用静态方法。调用方法除了这个指令外还有;invokespecial
、invokevirtual
、invokeinterface
。public Integer strToNumber(String str) { try { Integer var2 = Integer.parseInt(str); point("org.itstack.test.MethodTest.strToNumber", (Object)var2); return var2; } catch (Exception var3) { point("org.itstack.test.MethodTest.strToNumber", (Throwable)var3); throw var3; } }
MQ
等机制推送出去,用于图表展示监控信息。五、测试验证
ClassLoader
进行加载到执行。public static void main(String[] args) throws Exception { // 方法字节码增强 byte[] bytes = new MethodTest().getBytes(MethodTest.class.getName()); // 输出方法新字节码 outputClazz(bytes, MethodTest.class.getSimpleName()); // 测试方法 Class<?> clazz = new MethodTest().defineClass("org.itstack.test.MethodTest", bytes, 0, bytes.length); Method queryUserInfo = clazz.getMethod("strToNumber", String.class); // 正确入参;测试验证结果输出 Object obj01 = queryUserInfo.invoke(clazz.newInstance(), "123"); System.out.println("01 测试结果:" + obj01); // 异常入参;测试验证打印异常信息 Object obj02 = queryUserInfo.invoke(clazz.newInstance(), "abc"); System.out.println("02 测试结果:" + obj02); }
ASM字节码增强后类输出路径:/User/itstack/git/github.com/WormholePistachio/SQM/target/test-classes/MethodTestSQM.class 系统监控 :: [方法名称:org.itstack.test.MethodTest.strToNumber 输出信息:123] 01 测试结果:123 系统监控 :: [方法名称:org.itstack.test.MethodTest.strToNumber 异常信息:For input string: "abc"] Process finished with exit code 1
六、总结
JVM虚拟机
。至少不向以前那样只是去硬背一些理论,而是彻底的实践了。不要感觉这很难,嗯!ASM
,只是还没有注意到而已。最终多学习一些延申拓展的知识,关于这些技巧可以阅读 JVM虚拟机规范
,也可以阅读ASM文档;asm.itstack.org七、彩蛋
wiki
文档,同时逐步将各类案例汇总集中,方便获取。
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算