不改一行代码定位线上性能问题

背景
最近时运不佳,几乎天天被线上问题骚扰。前几天刚解决了一个 HashSet 的并发问题,周一又来了一个性能问题。

大致的现象是:
我们提供出去的一个 OpenAPI 反应时快时慢,快的时候几十毫秒,慢的时候几秒钟才响应。

尝试解决
由于这种也不是业务问题,不能直接定位。所以尝试在测试环境复现,但遗憾的测试环境贼快。

没办法只能硬着头皮上了。

中途有抱着侥幸心里让运维查看了 Nginx 里 OpenAPI 的响应时间,想把锅扔给网络。结果果然打脸了;Nginx 里的日志也表明确实响应时间确实有问题。

为了清晰的了解这个问题,我简单梳理了这个调用过程。

整个的流程算是比较常见的分层架构:

客户端请求到 Nginx。
Nginx 负载了后端的 web 服务。
web 服务通过 RPC 调用后端的 Service 服务。
日志大法

我们首先想到的是打日志,在可能会慢的方法或接口处记录处理时间来判断哪里有问题。

但通过刚才的调用链来说,这个请求流程不短。加日志涉及的改动较多而且万一加漏了还有可能定位不到问题。

再一个是改动代码之后还会涉及到发版上线。

工具分析

所以最好的方式就是不改动一行代码把这个问题分析出来。

这时就需要一个 agent 工具了。我们选用了阿里以前开源的 Tprofile 来使用。

只需要在启动参数中加入 -javaagent:/xx/tprofiler.jar 即可监控你想要监控的方法耗时,并且可以给你输出报告,非常方便。对代码没有任何侵入性同时性能影响也较小。

工具使用
下面来简单展示下如何使用这个工具。

首先第一步自然是 clone 源码然后打包,可以克隆我修改过的源码。

因为这个项目阿里多年没有维护了,还残留一些 bug,我在它原有的基础上修复了个影响使用的 bug,同时做了一些优化。

执行以下脚本即可。

git clone https://github.com/crossoverJie/TProfiler
mvn assembly:assembly
到这里之后会在项目的 TProfiler/pkg/TProfiler/lib/tprofiler-1.0.1.jar 中生成好我们要使用的 jar 包。

接下来只需要将这个 jar 包配置到启动参数中,同时再配置一个配置文件路径即可。

这个配置文件我 copy 官方的解释。

#log file name
logFileName = tprofiler.log
methodFileName = tmethod.log
samplerFileName = tsampler.log
#basic configuration items

开始取样时间

startProfTime = 1:00:00

结束取样时间

endProfTime = 23:00:00

取样的时间长度

eachProfUseTime = 10

每次取样的时间间隔

eachProfIntervalTime = 1
samplerIntervalTime = 20

端口,主要不要冲突了

port = 50000
debugMode = false
needNanoTime = false

是否忽略 get set 方法

ignoreGetSetMethod = true
#file paths 日志路径
logFilePath = /data/work/logs/tprofile/${logFileName}
methodFilePath =/data/work/logs/tprofile/${methodFileName}
samplerFilePath =/data/work/logs/tprofile/${samplerFileName}
#include & excludes items
excludeClassLoader = org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader

需要监控的包

includePackageStartsWith = top.crossoverjie.cicada.example.action

不需要监控的包

excludePackageStartsWith = com.taobao.sketch;org.apache.velocity;com.alibaba;com.taobao.forest.domain.dataobject
最终的启动参数如下:
-javaagent:/TProfiler/lib/tprofiler-1.0.1.jar
-Dprofile.properties=/TProfiler/profile.properties
为了模拟排查接口响应慢的问题,我用 cicada 实现了一个 HTTP 接口。其中调用了两个耗时方法:

这样当我启动应用时,Tprofile 就会在我配置的目录记录它所收集的方法信息。

我访问接口 http://127.0.0.1:5688/cicada-example/demoAction?name=test&id=10 几次后它就会把每个方法的明细响应写入 tprofile.log。
由左到右每列分别代表为:

线程ID、方法栈深度、方法编号、耗时(毫秒)。

但 tmethod.log 还是空的;

这时我们只需要执行这个命令即可把最新的方法采样信息刷到 tmethod.log 文件中。
java -cp /TProfiler/tprofiler.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 50000 flushmethod
flushmethod success
其中的端口就是配置文件中的 port。
再打开 tmethod.log :

不改一行代码定位线上性能问题
其中会记录方法的信息。

第一行数字为方法的编号。可以通过这个编号去 tprofile.log(明细)中查询每次的耗时情况。
行末的数字则是这个方法在源码中最后一行的行号。
其实大部分的性能分析都是统计某个方法的平均耗时。

所以还需要执行下面的命令,通过 tmethod.log tprofile.log来生成每个方法的平均耗时。

java -cp /TProfiler/tprofiler.jar com.taobao.profile.analysis.ProfilerLogAnalysis tprofiler.log tmethod.log topmethod.log topobject.log
print result success
打开 topmethod.log 就是所有方法的平均耗时。

4 为请求次数。
205 为平均耗时。
818 则为总耗时。
和实际情况是相符的。

方法的明细耗时

这是可能还会有其他需求;比如说我想查询某个方法所有的明细耗时怎么办呢?

官方没有提供,但也是可以的,只是要麻烦一点。

比如我想查看 selectDB() 的耗时明细:

首先得知道这个方法的编号,在 tmethod.log 中可以看查到。

2 top/crossoverjie/cicada/example/action/DemoAction:selectDB:84

编号为 2.

之前我们就知道 tprofile.log 记录的是明细,所以通过下面的命令即可查看。

grep 2 tprofiler.log

通过第三列方法编号为 2 的来查看每次执行的明细。

但这样的方式显然不够友好,需要人为来过滤干扰,步骤也多;所以我也准备加上这样一个功能。

只需要传入一个方法名称即可查询采集到的所有方法耗时明细。

总结
回到之前的问题;线上通过这个工具分析我们得到了如下结果。

有些方法确实执行时快时慢,但都是和数据库相关的。由于目前数据库压力较大,准备在接下来进行冷热数据分离,以及分库分表。
在第一步操作还没实施之前将部分写数据库的操作改为异步,减小响应时间。
考虑接入 pinpoint 这样的 APM工具。
类似于 Tprofile 的工具确实挺多的,找到适合自己的就好。

在还没有使用类似于 pinpoint 这样的分布式跟踪工具之前应该会大量依赖于这个工具,所以后续说不定也会做一些定制,比如增加一些可视化界面等,可以提高排查效率。

你的点赞与分享是对我最大的支持

原文地址:http://blog.51cto.com/13842645/2316082

时间: 2024-10-13 06:21:20

不改一行代码定位线上性能问题的相关文章

微服务中定位线上问题

微服务架构下的程序一般有多个节点提供服务,用户请求不一定落在哪一个节点,如果节点 存在问题,一般利用日志监控系统来确认问题. 日志监控系统提供实时日志,以及全文检索日志,并且日志实时查询以及全文检索查询都要 以倒叙查询. 中间件系统或业务系统对于日志生成的级别,debug.info.error等级别,以及error日志 要打印详细日志栈信息. 通过合理详细的使用日志以及配合日志监控系统的实时日志以及日志检索功能一般能够很快地 定位问题,定位到问题一般就能很快地解决. 错误日志尽量打印栈信息,in

利用Arthas定位线上问题实例

前言 Arthas是一个类似于Btrace的JVM在线调试分析工具,具体可参考我之前写的一篇博客:利用JVM在线调试工具排查线上问题.本文分享笔者刚遇到的一个问题,虽然不复杂,但是很典型. 问题与分析过程 昨天上线遇到一个问题,交易后给大数据平台异步送数,但是他们说没收到数据,因为我们没有打日志,所以没有直接的证据证明是他们的问题而不是我们的问题. 送数的原理大致如下,就是交易主线程把数据放到队列里,然后异步线程从队列里把数据取出来,发送到后台. 队列: BlockingQueue<Messag

阿里问题定位神器 Arthas 的骚操作,定位线上BUG,超给力

背景公司有个渠道系统,专门对接三方渠道使用,没有什么业务逻辑,主要是转换报文和参数校验之类的工作,起着一个承上启下的作用. 最近在优化接口的响应时间,优化了代码之后,但是时间还是达不到要求:有一个诡异的100ms左右的耗时问题,在接口中打印了请求处理时间后,和调用方的响应时间还有差了100ms左右.比如程序里记录150ms,但是调用方等待时间却为250ms左右. 下面记录下当时详细的定位&解决流程(其实解决很简单,关键在于怎么定位并找到解决问题的方法) 定位过程分析代码渠道系统是一个常见的spr

jstack定位线上CPU过高问题

top  查看占用资源最高进程的PID jstack -l  pid  >  statck.log   输出线程堆栈信息 top -H -p pid   找出相对应的线程TID printf "%x \n" <tid>  输出十六进制 less  statck.log  查看日志文件,找到线程16进制关键字,上下翻页查看与代码相关的信息,定位代码问题 原文地址:https://www.cnblogs.com/byfboke/p/12681632.html

jquery areapicker 地区选择器 为手机浏览器度身定做. 一行代码完成手机上的地区选择

使用方法: 1 <!doctype html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title>areapicker demo</title> 6 <meta name="viewport" content="w

线上性能问题初步排查方法

文章出处http://ifeve.com/find-bug-online/ 有时候有很多问题只有在线上或者预发环境才能发现,而线上又不能Debug,所以线上问题定位就只能看日志,系统状态和Dump线程,本文只是简单的介绍一些常用的工具,帮助定位线上问题. 问题定位 1: 首先使用TOP命令查看每个进程的情况,显示如下: top - 22:27:25 up 463 days, 12:46, 1 user, load average: 11.80, 12.19, 11.79 Tasks: 113 t

一行代码搞定Adapter

15年Google I/O大会发不了三个重要支持库 >Material design (Android Support Design) >百分比布局:Percent support lib >数据绑定: Data Binding Library 如果你还不没用过Data Bind Library 没关系 那你知道ButterKnife吧 DataBindLibrary 和ButterKnife都是编译时期生成相应的注解文件 因此 在性能上不会有什么大的影响 因此放心的尝试吧.(个人感觉D

“你的这个只要一行代码就能搞定”

帮舍友代写了一个程序,中期检查的时候和他一起去见老师.老师看了说我们的工作量太少.这个几分钟就搞定.最后他居然说:"这个工作量太少了,就调用一行代码的事,几分钟就可以搞定".去之前我知道这个临时做出来的东西,肯定没跟上进度.但好歹有几天的工作量吧.他居然说只要几分钟,一行调用代码.难道用http协议从网上获取天气数据,再解析xml格式的数据,再转换字符串编码,再根据天气选择要绘制的图片和文字,只需要一行代码?难道我不需要查资料,不需要设计界面吗?为了用mfc做出这个界面,我确实费了一番

iOS开发一行代码系列:一行搞定输入框

最近总结了下开发过程中常用的功能,发现有时候我在做重复性的劳动.于是决定把常用的功能抽出来,方便下次使用. 我的想法是:用最少的代码来解决问题.于是写了一些常用的工具类,名字就叫一行代码系列吧...好像挺挫的.. 大致内容有: 1.一行搞定输入框 2.一行搞定网络请求 3.一行搞定上下拉刷新(会自动判断是上拉还是下拉还是两者并存) 4.一行搞定数据库(最近还在写,功能已经基本实现) 5.一行搞定图片保存 6.一行搞定定位 7.一行搞定网络状况变化 8.一行搞定X(功能小集合) 一行搞定输入框 输