刚注册在域名可以自己做网站吗,discuz论坛使用方法,替人做赌彩网站被判刑,我会编程怎么做网站简介#xff1a; 有了实际的使用之后#xff0c;不免会想到#xff0c;Arthas 是如何做到在程序运行时#xff0c;动态监测我们的代码的呢#xff1f;带着这样的问题#xff0c;我们一起来看下 Java Agent 技术实现原理。 背景介绍
项目中有使用到 com.github.dream…简介 有了实际的使用之后不免会想到Arthas 是如何做到在程序运行时动态监测我们的代码的呢带着这样的问题我们一起来看下 Java Agent 技术实现原理。 背景介绍
项目中有使用到 com.github.dreamroute excel-helper 这个工具来辅助 Excel 文件的解析出错时的代码是这样写的如下所示(非源代码) try { excelDTOS ExcelHelper.importFromFile(ExcelType.XLSX, file, ExcelDTO.class); } catch (Exception e) { log.error(ExcelHelper importFromFile exception msg {}, e.getMessage()); }
因为打印异常信息时使用了 e.getMessage() 方法没有将异常信息打印出来。而且本地复现也没有复现出来。所以只能考虑使用 arthas 来协助排查这个问题了。
排查过程
1、线上服务器安装 Arthas。https://arthas.aliyun.com/doc/install-detail.html
2、使用 watch 命令监控指定方法打印出异常的堆栈信息命令如下
watch com.github.dreamroute.excel.helper.ExcelHelper importFromFile {params,throwExp} -e -x 3
再次调用方法捕获到异常栈信息如下 已经捕获到异常并打印出堆栈信息。
3、根据对应的堆栈信息定位到具体的代码如下 代码很简单从代码中可以很清晰的看到如果没有从 headerInfoMap 中没有获取到指定的 headerInfo 就会抛这个异常。没有找到只有两种情况
headerInfoMap 中保存的信息不对。cell 中的 columnIndex 超出的正常的范围导致没有获取到对应 HeaderInfo 。
对于第二种情况首先去校验了一下上传的 Excel 文件是否有问题本地测试了一下 Excel 文件没有任何问题。本地测试也是成功的所以主观判断第二种情况的可能性不大。
所以说主要检查第一种情况是否发生,这个时候可以再去看一下该方法的第一行代码
MapheaderInfoMap processHeaderInfo(rows,cls);
可以看到headerInfoMap是通过processHeaderInfo中获取的。找到processHeaderInfo 的代码如下所示。
public static MapproceeHeaderInfo(Iteratorrows, Class cls) {if (rows.hasNext()) {Row header rows.next();return CacheFactory.findHeaderInfo(cls, header);}return new HashMap(0);
}
public static MapfindHeaderInfo(Class cls, Row header) {MapheaderInfo HEADER_INFO.get(cls);if (MapUtils.isEmpty(headerInfo)) {headerInfo ClassAssistant.getHeaderInfo(cls, header);HEADER_INFO.put(cls, headerInfo);}return headerInfo;
}
public static MapgetHeaderInfo(Class cls, Row header) {IteratorcellIterator header.cellIterator();Listfields ClassAssistant.getAllFields(cls);MapheaderInfo new HashMap(fields.size());while (cellIterator.hasNext()) {org.apache.poi.ss.usermodel.Cell cell cellIterator.next();String headerName cell.getStringCellValue();for (Field field : fields) {Column col field.getAnnotation(Column.class);String name col.name();if (Objects.equals(headerName, name)) {HeaderInfo hi new HeaderInfo(col.cellType(), field);headerInfo.put(cell.getColumnIndex(), hi);break;}}}return headerInfo;
}
主要通过 CacheFactory 类的 findHeaderInfo 来生成在 findHeaderInfo 方法中通过一个被 static final 修饰的 HEADER_INFO 变量来做缓存被调用时先去HEADER_INFO 中查如果有则直接返回没有则重新创建也就说明相同的 Excel 文件仅初始化一次 HeaderInfo 。创建的步骤在 ClassAssistant.getHeaderInfo() 方法中。
简单的看一下 HeaderInfo 的生成过程根据 Excel 文件的第一行中的各个 Cell 值与自定义实体类的注解比较如果名字相同就存为一个键值对( HeaderInfo 的数据结构为 HashMap )。
4、这个时候需要再确认一下 HEADER_INFO 中保存的 ExcelDTO.class 相关的 HeaderInfo 是怎样的。通过 ognl 命令或者 getstatic 命令来查看。这里使用 ognl 命令。
ognl #valuenew com.tom.dto.ExcelDTO(),#valueMapcom.github.dreamroute.excel.helper.cache.CacheFactoryHEADER_INFO,#valueMap.get(#value.getClass()).entrySet().iterator.{#this.value.name}
结果如下正常情况下这个 Excel 文件有 6 列信息为什么只产生了 4 个键值对呢如果 HEADER_INFO 中保存了错的从上面的逻辑来看后面上传的正确的 Excel 文件在解析时都会抛错。 5、询问了当时发现这个问题的同事得知他第一次上传的 Excel 文件是有问题的后面想改正再上传时便出现了问题。到这里问题也算是找到了。
Arthas 原理探究
有了实际的使用之后不免会想到Arthas 是如何做到在程序运行时动态监测我们的代码的呢带着这样的问题我们一起来看下 Java Agent 技术实现原理。
Java Agent 技术
Agent 是一个运行在目标 JVM 的特定程序它的职责是负责从目标 JVM 中获取数据然后将数据传递给外部进程。加载 Agent 的时机可以是目标 JVM 启动之时也可以是在目标 JVM 运行时进行加载而在目标 JVM 运行时进行 Agent 加载具备动态性。
基础概念
JVMTIJVM Tool Interface是 JVM 暴露出来的一些供用户扩展的接口集合JVMTI 是基于事件驱动的JVM 每执行到一定的逻辑就会调用一些事件的回调接口如果有的话这些接口可以供开发者去扩展自己的逻辑。JVMTIAgentJVM Tool Interface是一个动态库利用 JVMTI 暴露出来的一些接口帮助我们在程序启动时或程序运行时 JVM Attach 机制将 Agent 加载到目标 JVM 中。JPLISAgentJava Programming Language Instrumentation Services Agent它的作用是初始化所有通过 Java Instrumentation API 编写的 Agent并且也承担着通过 JVMTI 实现 Java Instrumentation 中暴露 API 的责任。VirtualMachine 提供了Attach 动作和 Detach 动作允许我们通过 attach 方法远程连接到 JVM 上然后通过 loadAgent 方法向 JVM 注册一个代理程序 agent 在该 agent 的代理程序中会得到一个 Instrumentation 实例该实例可以在 class 加载前改变 class 的字节码也可以在 class 加载后重新加载。Instrumentation可以在 class 加载前改变 class 的字节码premain也可以在 class 加载后重新加载agentmain。
执行过程
动手写一个 Demo
通过 javassist在运行时更改指定方法的代码在方法之前后添加自定义逻辑。
1、定义 Agent 类。当前 Java 提供了两种方式可以将代码代码注入到 JVM 中这里我们的 Demo 选择使用 agentmain 方法来实现。
premain在启动时通过 javaagent 命令将代理注入到指定的 JVM 中。 agentmain运行时通过 attach 工具激活指定代理。
/*** AgentMain** author tomxin*/
public class AgentMain {public static void agentmain(String agentArgs, Instrumentation instrumentation) throws UnmodifiableClassException, ClassNotFoundException {instrumentation.addTransformer(new InterceptorTransformer(agentArgs), true);Class clazz Class.forName(agentArgs.split(,)[1]);instrumentation.retransformClasses(clazz);}
}/*** InterceptorTransformer** author tomxin*/
public class InterceptorTransformer implements ClassFileTransformer {private String agentArgs;public InterceptorTransformer(String agentArgs) {this.agentArgs agentArgs;}Overridepublic byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {//javassist的包名是用点分割的需要转换下if (className ! null className.indexOf(/) ! -1) {className className.replaceAll(/, .);}try {//通过包名获取类文件CtClass cc ClassPool.getDefault().get(className);//获得指定方法名的方法CtMethod m cc.getDeclaredMethod(agentArgs.split(,)[2]);//在方法执行前插入代码m.insertBefore({ System.out.println(\开始执行\); });m.insertAfter({ System.out.println(\结束执行\); });return cc.toBytecode();} catch (Exception e) {}return null;}
}
2、使用 Maven 配置 MANIFEST.MF 文件该文件能够指定 Jar 包的 main 方法。
buildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-jar-plugin/artifactIdversion2.3.1/versionconfigurationarchivemanifestaddClasspathtrue/addClasspath/manifestmanifestEntriesAgent-Classcom.tom.mdc.AgentMain/Agent-ClassCan-Redefine-Classestrue/Can-Redefine-ClassesCan-Retransform-Classestrue/Can-Retransform-Classes/manifestEntries/archive/configuration/plugin/plugins/build
3、定义 Attach 方法通过 VirtualMachine.attach(#{pid}) 来指定要代理的类。
import com.sun.tools.attach.VirtualMachine;import java.io.IOException;/*** AttachMain** author tomxin*/
public class AttachMain {public static void main(String[] args) {VirtualMachine virtualMachine null;try {virtualMachine VirtualMachine.attach(args[0]);// 将打包好的Jar包添加到指定的JVM进程中。virtualMachine.loadAgent(target/agent-demo-1.0-SNAPSHOT.jar,String.join(,, args));} catch (Exception e) {if (virtualMachine ! null) {try {virtualMachine.detach();} catch (IOException ex) {ex.printStackTrace();}}}}
}
4、定义测试的方法
package com.tom.mdc;
import java.lang.management.ManagementFactory;
import java.util.Random;
import java.util.concurrent.TimeUnit;/*** PrintParamTarget** author toxmxin*/
public class PrintParamTarget {public static void main(String[] args) {// 打印当前进程IDSystem.out.println(ManagementFactory.getRuntimeMXBean().getName());Random random new Random();while (true) {int sleepTime 5 random.nextInt(5);running(sleepTime);}}private static void running(int sleepTime) {try {TimeUnit.SECONDS.sleep(sleepTime);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(running sleep time sleepTime);}
}
原文链接 本文为阿里云原创内容未经允许不得转载。