相信很多人都有这样的感受,自己写的代码在开发测试环境中运行稳定,但是上线的时候,就是抽搐。不是漏了就是漏了反正就是报错而已,在线调试代码很麻烦很头疼。不过,阿里巴巴发布了一个工具叫Arthas,可以在线分析诊断Java代码,让人印象深刻。阿尔萨斯是什么?Arthas(阿尔萨斯)是阿里开源的一款Java在线分析诊断工具。Arthas能解决什么问题?在日常的开发上线过程中,我们或多或少会遇到以下问题,因为无法在线调试。我有点尴尬。”这个类是从哪个jar包加载的?为什么会报各种类型的相关Exception?为什么我改的代码没有执行?难道是我没犯?分支错了?如果遇到问题不能在线调试,难道只能加日志重新发布吗?我在线上遇到某用户数据处理的问题,但是同样在线无法调试,离线也无法重现!是否有系统健康状况的全局视图?有什么办法可以监控JVM的实时运行状态吗?在线代码有错误不想重新发布?你能改变类文件并替换它吗?安装启动Arthas的两种方式1.jar包启动wgethttps://alibaba.github.io/arthas/arthas-boot.jarjava-jararthas-boot.jar--target-ip0.0.0.0首先我要要使用arthas调试项目,服务器必须有正在运行的Java服务。demo-0.0.1-SNAPSHOT.jar是我启动的测试项目。启动arthas后,会自动检测本地所有Java服务并列出。我们只需要根据序列号进入需要的Java服务即可。可以调试工程,选择1进入对应进程的arthas交互平台[root@iz2zehzeir87zi8q99krk1zdata]#java-jararthas-boot.jar--target-ip172.17.72.201[INFO]arthas-bootversion:3.1.0[INFO]Foundexistingjavaprocess,pleasechooseoneandhitRETURN.*[1]:28679demo-0.0.1-SNAPSHOT.jar2,在线安装curl-Lhttps://alibaba.github.io/arthas/install.sh|sh执行以上命令即可在所在文件生成as.sh执行文件并启动arthas./as.shPID#Processid指定JAVA进程id./as.sh-h#h获取更多参数信息3.远程连接:》使用arthas服务的web控制台,必须对外暴露本地ip"java-jararthas-boot.jar--target-ip172.17.72.201java-jararthas-boot.jar--telnet-port9999--http-port-1./as.sh--target-ip0.0.0.0./as.sh--telnet-port9999--http-port-1还有两种方式可以访问arthas控制台》(1),webconsoleinterface”和“重点说明”:--target-ip的ip必须是arthas所在机器对外暴露的ip,“但是如果你用的是阿里云机器,必须使用私有ip启动arthas服务,但访问必须是公网IP》(二)、telnet方法》telnet10.0.2.58563访问http://59.110.218.9:8563/,进入交互平台Arthas命令使用1、Dashboard命令查看当前系统的实时数据面板,例如:服务器Thread信息、内存内存、GC回收等2.Thread(线程监控)$thread-n3"as-command-execute-daemon"Id=57cpuUsage=72%RUNNABLEatsun.management.ThreadImpl.dumpThreads0(NativeMethod)atsun.management。ThreadImpl.getThreadInfo(ThreadImpl.java:448)atcom.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:133)atcom.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:79)atcom.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)atcom.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)atcom.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)atcom.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)atcom.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:370)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)atjava.lang.Thread.run(Thread.java:748)Numberoflockedsynchronizers=1-java.util.concurrent.ThreadPoolExecutor$Worker@a2f70c7"可以看到这个线程是由synchronized关键字引起的lockBlocking”,目前只支持找??出被synchronized关键字阻塞的线程,如果是java.util.concurrent.Lock暂不支持Numberoflockedsynchronizers=1-java.util.concurrent.ThreadPoolExecutor$Worker@a2f70c7thread-n3#当前最忙的前N个线程thread-b,##找出当前阻塞其他线程的线程thread-n3-i1000#一定时间后显示“重点学习”:thread-b,##“找出当前阻塞其他线程的线程”3.JVM(jvm实时运行状态、内存使用情况等)$jvmRUNTIME---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------MACHINE-NAME28679@iz2zehzeir87zi8q99krk1zJVM-START-TIME2019-03-2817:32:16MANAGEMENT-SPEC-VERSION1.2SPEC-NAMEJavaVirtualMachineSpecificationSPEC-供应商OracleCorporationSPEC-VERSION1.8VM-NAMEJavaHotSpot(TM)64-BitServerVMVM-VENDOROracleCorporationVM-VERSION25.191-b12INPUT-ARGUMENTS[]CLASS-PATHdemo-0.0.1-SNAPSHOT.jarBOOT-CLASS-PATH/usr/本地/jdk/jre/lib/resources.jar:/usr/local/jdk/jre/lib/rt.jar:/usr/local/jdk/jre/lib/sunrsasign.jar:/usr/local/jdk/jre/lib/jsse.jar:/usr/local/jdk/jre/lib/jce.jar:/usr/local/jdk/jre/lib/charsets.jar:/usr/local/jdk/jre/lib/jfr.jar:/usr/local/jdk/jre/classesLIBRARY-PATH/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib4,trace(当前方法的内部调用路径,路径上每个节点的耗时)$trace#classname#methodname相对longforexecution该方法会高亮调用链接的耗时属性,方便排查。参数-j可以过滤jdk函数trace-jcom.example.demo.controllerindex2参数#cost可以过滤trace的执行时间,以毫秒为单位ms-jcom.example.demo.controllerindex2'#cost>10'5.watch当前方法执行数据观察,可观察范围为:返回值、抛出的异常、入参$trace#类名#方法名"{params,target,returnObj,throwExp}"OGNL表达式{params,target,returnObj,throwExp}throwExp:异常参数:输入参数(数组),单参数params【0】returnObj:返回值$watchcom.example.demo.controllerindex2"{params,target,returnObj}"-x5PressQorCtrl+Ctoabort.Affect(class-cnt:1,method-cnt:1)costin81ms.ts=2019-03-2914:24:14;[cost=1000.746582ms]result=@ArrayList[@Object[][@String[新知府],],@controller[],@String[index2],]6.栈中当前方法调用的路径,显示当前方法被调用的那些方法publicstaticStringuuidOne(){returnuuidTwo();}publicstaticStringuuidTwo(){returnUUID.randomUUID().toString().replaceAll("-","");}$stackcom.example.demo.controlleruuidTwoPressQorCtrl+Ctoabort.Affect(class-cnt:1,method-cnt:1)costin58ms.ts=2019-03-2914:38:19;thread_name=http-nio-8888-exec-5;id=13;is_daemon=true;优先级=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@525b461a@com.example.demo.controller.uuidOne()在com.example.demo.controller.index2(controller.java:31)atsun.reflect.GeneratedMethodAccessor36.invoke(null:-1)atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)7。监控类和方法调用的监控命令,调用次数,成功次数,失败次数,平均响应时间,失败率等$monitor-c4com.example.demo.controlleruuidTwoPressQorCtrl+Ctoabort.Affect(class-cnt:1,method-cnt:1)costin56ms.timestampclassmethodtotalsuccessfailavg-rt(ms)失败率-------------------------------------------------------------------------------------------------2019-03-2914:55:40com.example.demo.controlleruuidTwo7700.180.00%8.classloader命令统计JVM中所有的类加载器,并以树的形式展示.com.taobao.arthas.agent.ArthasClassloader@5fc476c6+-com.taobao.arthas.agent.ArthasClassloader@5017e14b+-sun.misc.Launcher$AppClassLoader@5c647e05+-java.net.FactoryURLClassLoader@4ad317f0+-work.springframe.loader.Launched@URLClassLoader20ad9418Affect(row-cnt:7)costin5ms在线代码热更新(动态修改在线项目代码)代码中手动抛出异常,“修改在线代码不停机不重发包”启动服务即可也遇到了我们预期的异常“替换代码的过程:”1、jad命令先反编译需要改的文件,保存,编译器修改$jad--source--onlycom.example.demo.DemoApplication>/数据/演示A申请.java在这里插入图片描述修改后需要重新加载类到JVM2,通过SC命令查找当前类是由哪个classLoader加载的。编译内存中的类$mc-c20ad9418/data/DemoApplication.java-d/dataMemorycompileroutput:/data/com/example/demo/DemoApplication.class4,redefine命令将编译后的类加载到JVM中编译后的.class文件地址$redefine/data/com/example/demo/DemoApplication.classredefinesuccess,size:1"替换文件后,我们再次访问程序,发现异常没有了,程序修改正确,class文件替换成功”总结是这样的我们在生产环境中使用arthas进行了不停机不发包的Java代码替换。功能确实比较强大。本文只是揭示了arthas强大功能的冰山一角。后面会出更详细的文章,让大家一起偷懒学习更多。artha越用越勤,整体功能很强大,就是命令行的输入法让我很头疼。随着年龄的增长,我的记忆力真的下降了,作为一个懒惰的程序员,我不得不记住那么多命令和指令。这些参数简直要了我的命。再次,因为懒惰让我努力工作,我决定做一个arthas命令可视化平台。《设计初衷》:设计这个平台的初衷很简单,就是让程序员更专注于解决问题,而不是死记硬背那么多枯燥的命令。我不是一个愿意死记硬背的人。我觉得我们应该把更多有趣和有意义的东西放在我的脑海里。可能在用惯了命令行的大佬眼里,这个功能比较没用,甚至有点多余,但毕竟还有更多像我这样的普通人,每天还陷在重复性的工作中,而工作量可以减少一点。别紧张。
