性能压测-时间方法定位-Arthas使用
性能测试:最强悍的工具终于上场了arthas,解答无论给服务器多大压力,服务器的CPU利用率始终很低,吞吐量一直上不去?如果瓶颈是由于代码问题导致的,请问如何定位?
Arthas 是由阿里巴巴开源,在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。(Arthas官方地址)
一、Arthas 的六大特性
Dashboard:实时查看Java服务的运行状况;
查看入参/返回值/异常:可实时查看方法调用的入参、返回值、异常;
在线热更新:jad -> sc -> redefine 一条龙热更新代码;
类冲突:秒解类冲突问题,定位类加载路径;
性能热点:快速定位应用的热点,生成火焰图;
Webconsole:在线诊断,点开网页及时诊断线上应用;
使用dashboard命令可以获取当前Java服务的运行状态信息,如线程状况,GC情况等。
Arthas抓下哪个方法耗时长更方便定位 最喜欢使用arthas看子方法耗时累死不疲。
通过主方法定位更深入的一些子方法的办法。
最强悍的工具终于上场了——arthas,官方帮助文档:https://arthas.aliyun.com/doc/,详细的功能和使用上面说的非常清楚。
1)下载
登录被测服务器,在命令行下输入命令:
wget https://arthas.aliyun.com/arthas-boot.jar
2)启动压测
重新运行第1步的场景,开始压测服务器。
3)启动arthas
在命令行下面执行(使用和目标进程一致的用户启动,否则可能attach失败):
java -jar arthas-boot.jar
然后选择要监控的进程,输入1

安装命令:
curl -L https://alibaba.github.io/arthas/install.sh | bash
安装某个线程 (有些线程内存太大,需要重启才能安装完成)
./as.sh [<pid>]
不传pid就列出所有java进程,自己输数字选择
上述已经安装好,然后选择该方法:
定位该方法下的某一个节点:
com.Axxx.midend.app.config.web.server.facade.AppConfigFacade#listConfigInfo,想定位的方法名:
'#cost>100'是代表耗时大于100ms
命令是:
trace com.Axxx.midend.app.config.web.server.facade.AppConfigFacade listConfigInfo '#cost > 100'
2.定位方法的全部类:
trace cn.asae.e.contract.web.ContractController getContract -j-j 过滤掉jdk方法__________________________________________________________________________________________________________________________________________________________________| 时间戳 线程名称 线程id 是否守护进程 优先级 线程上下文类加载器 | `---ts=2019-10-21 16:33:13;thread_name=http-nio-37080-exec-5;id=52;is_daemon=true;priority=5;TCCL=org.apache.catalina.loader.WebappClassLoader@67fff846 || 调用信息 - 方法拦截器 - 目标方法| `---[1483.931493ms] cn.asae.e.contract.web.ContractController$$EnhancerBySpringCGLIB$$feabdb7f:getContract() || `---[1483.715988ms] org.springframework.cglib.proxy.MethodInterceptor:intercept() #0 || `---[1482.519392ms] cn.asae.e.contract.web.ContractController:getContract() || +---[0.03355ms] cn.asae.core.common.response.v2.ResponseBodyV2:<init>() #243 || +---[0.016195ms] cn.asae.core.common.token.TokenCache:getInstance() #246 || +---[0.01442ms] cn.asae.core.common.token.TokenCache:get() #246 || +---[0.02442ms] cn.asae.e.common.model.Op:createOp() #246 || +---[161.579687ms] cn.asae.e.contract.service.ContractService:getContract() #249 || +---[164.980125ms] cn.asae.e.contract.service.ContractService:getContractOcrs() #251 || +---[0.036474ms] com.google.common.collect.Maps:newHashMap() #255 || +---[158.223852ms] cn.asae.e.contract.service.ContractService:getContractMembers() #260 || +---[0.025848ms] com.google.common.collect.Lists:newArrayList() #263 || +---[0.024837ms] cn.asae.e.contract.model.ContractVO:getContractNum() #264 || +---[160.942618ms] cn.asae.e.contract.service.ContractService:getContracts() #266 || +---[0.024172ms] org.apache.commons.collections.MapUtils:isNotEmpty() #271 || +---[198.281335ms] cn.asae.e.organization.service.AccountService:getAccountLite() #291 || +---[159.761087ms] cn.asae.e.organization.service.DepartmentService:getDepartment() #295 || +---[0.009013ms] org.apache.commons.collections.MapUtils:isNotEmpty() #297 || +---[0.013967ms] cn.asae.e.contract.model.ContractVO:getSignId() #312 || +---[0.006482ms] com.google.common.collect.Lists:newArrayList() #312 || +---[160.503511ms] cn.asae.e.organization.service.AccountService:getAccountLite() #312 || +---[0.015528ms] cn.asae.e.contract.model.ContractVO:getDepartmentId() #316 || +---[0.006504ms] com.google.common.collect.Lists:newArrayList() #316 || +---[159.023976ms] cn.asae.e.organization.service.DepartmentService:getDepartment() #316 || +---[0.005827ms] cn.asae.e.contract.model.ContractVO:getSignId() #319 || +---[0.017222ms] cn.asae.e.organization.model.Account:<init>() #319 || +---[0.0142ms] cn.asae.e.organization.model.Account:getFullname() #319 || +---[0.012931ms] cn.asae.e.contract.model.ContractVO:setSignName() #319 || +---[0.00492ms] cn.asae.e.contract.model.ContractVO:getDepartmentId() #321 || +---[0.010856ms] cn.asae.e.organization.model.DepartmentVO:<init>() #321 || +---[0.018637ms] cn.asae.e.organization.model.DepartmentVO:getName() #323 || +---[0.011019ms] cn.asae.e.contract.model.ContractVO:setDepartmentName() #323 || +---[0.010145ms] cn.asae.e.organization.model.DepartmentVO:getOrganizationName() #324 || +---[0.010541ms] cn.asae.e.contract.model.ContractVO:setOrganizationName() #324 || +---[0.005493ms] com.google.common.collect.Maps:newHashMap() #326 || +---[0.014093ms] cn.asae.core.common.response.v2.ResponseBodyV2:init() #333 || +---[145.783843ms] cn.asae.e.contract.service.ContractService:addContractLog() #335 || `---[0.386367ms] cn.asae.core.common.response.v2.ResponseBodyV2:toResponse() #342 ||________________________________________________________________________________________________________________________________________________________________|
4)查看dashboard
输入dashboard,按回车,会展示当前进程的信息,按ctrl+c可以中断执行。

图中出现了大量的红色BLOCKED(阻塞)线程,意思是因为竞争某一个资源导致了线程阻塞,压力肯定上不去呀,非常清晰。
6、定位瓶颈
通过thread命令来获取到BLOCKED状态的线程栈信息。
命令行输入:thread 70

发现这个线程持有这把锁,没干活却在睡觉,接下来反编译或者直接找开发看源码,弄明白这个方法具体在做什么,瓶颈一目了然。发现瓶颈的源码的样子给大家看一下:

当然,这样的瓶颈定位直接使用jstack也是完全可以做到的,只是arthas的功能更强大而已,课堂上结合更复杂的例子用的更多些。