引言
在应用Arthas的过程中,感觉有些性能很好用,然而命令行的用起来会比拟麻烦,,本着学习的态度,本人设计了一套无需停机,埋点,热修复,监控等性能的利用,并提供了WEB页面进行操作,并且会一直集成一些罕用的性能 .
1. 曾经反对的操作
- 监控JVM、GC、启动参数、线程、零碎参数等大盘信息
- 对于正在运行中的程序,动静给某个办法织入代码,获取某个函数的执行工夫
- 对于正在运行中的程序,动静给某个办法织入代码, 获取某个controller办法申请参数和返回值
- 对于正在运行中的程序,获取全副线程占有CPU工夫百分比,并反对查看指定线程运行的堆栈信息
- 热修复,通过反编译导出java文件,批改后再导入通过classload重载入实现热编译(实用于空指针等bug,大量批改代码就可解决问题时应用)
- 因为应用web页面操作,ip须要改为指标过程机器ip(默认是localhost如果是本地运行无需批改)
关键技术
技术名称 | 作用 |
---|---|
Javaagent | 在运行前加载,或在运行中对字节码进行操作 |
VirtualMachine | 在运行中附着到指标过程上,并调用agent |
架构局部
工程 | 技术 |
---|---|
web | Vue + Ant design |
server | Springboot+Websocket(当前可能弃用Springboot改为NettyServer) |
agent | netty + Javassist + cfr + JavaCompiler |
2. 如何装置应用?
- 去阿里云Maven仓库下载Jar包即可
(maven默认国外仓库)
(阿里云Maven仓库) 举荐应用!
Github源码
搜寻nobugboy或ETrace即可
获取到下载地址,他这个链接会动静刷新,所以须要本人去搜寻下再下载。
- 获取到连贯后
1\. win间接点击连贯下载,mac/linux wget连贯2\. 将文件改名为 ETrace-1.2.4.jar (点连贯下载不必改)
- 运行
linux/macos 具体由你配置的环境变量名称作为值,个别为$JAVA_HOMEwindows $JAVA_HOME间接替换为javahome的相对目录即可java -jar -Xbootclasspath/a:$JAVA_HOME/lib/tools.jar ETrace-1.2.4.jar
3. gif演示操作
- 演示大盘和线程追踪,点击线程间接能够获取以后堆栈信息(定位高CPU起因)
- 演示织入打印执行工夫(为了难看我睡眠了1s)
- 演示织入打印参数返回值
4. 热修复,这个性能录制太繁琐,间接图文吧
场景:一个同学提交了空指针异样的代码到了生产环境,测试没有笼罩到。
@GetMapping(value = "/get/{id}",name = "依据id获取用户") public User test(@PathVariable(value = "id",name = "id") String id){ User user = null; user.setName("测试"); return user; } #调用一下试试 curl localhost:8080/get/1 出错了! exception is java.lang.NullPointerException at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-5.3.6.jar:5.3.6] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.6.jar:5.3.6]
此时服务曾经在运行状态,在不能重启服务的状况下咱们来修复,先将须要批改的类门路找到,输入到桌面(输入门路肯定要在最初加 /)
批改空指针问题,编辑导出的文件
/* * Decompiled with CFR. * * Could not load the following classes: * com.example.springboottestdemo.controller.User */package com.example.springboottestdemo.controller;import com.example.springboottestdemo.controller.User;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RestController;@RestController(value="\u7528\u6237\u76f8\u5173\u63a5\u53e3")public class TestController { @GetMapping(value={"/last"}, name="get\u8bf7\u6c42\u6d4b\u8bd5\u53c2\u6570\u63cf\u8ff0") public User test2(User user) { return user; } @PostMapping(value={"/post"}, name="post\u8bf7\u6c42\u6d4b\u8bd5\u53c2\u6570\u63cf\u8ff0") public User test1(@RequestBody User user) { System.out.println((Object)user); return user; } @GetMapping(value={"/get/{id}"}, name="\u6839\u636eid\u83b7\u53d6\u7528\u6237") public User test(@PathVariable(value="id", name="id") String id) { //将此处修复 User user = new User(); user.setName("\u6d4b\u8bd5"); return user; }}
看到控制台 "compiler ok"字样代表编译实现,接下来从新load进去
看到控制台 "redefine successful !"字样代表load胜利,此时咱们再次申请方才报错的接口,谬误隐没代表胜利修复。
curl localhost:8080/get/1{"id":null,"name":"测试","us":null,"type":null}%