阿里開源工具 TProfiler --在海量業務代碼中精確定位性能代碼

如何使用 JDK 自身提供的工具進行 JVM 調優將 TPS 由 2.5 提升到 20 (提升了 7 倍),並準確定位系統瓶頸:我們應用裏靜態對象不是太多、有大量的業務線程在頻繁創建一些生命週期很長的臨時對象,代碼裏有問題。那麼問題來了,如何在海量業務代碼裏邊準確定位這些性能代碼?本文將介紹如何使用阿里開源工具 TProfiler 來定位這些性能代碼,成功解決掉了 GC 過於頻繁的性能瓶頸,並最終在上次優化的基礎上將 TPS 再提升了4 倍,即提升到 100。

1. TProfiler 的下載安裝

1.1. 下載

訪問 TProfiler 的 GitHub 主頁,點擊 Clone or download 按鈕的打開下載選項,點擊該選項下的 Download ZIP 按鈕將 TProfiler-master.zip 下載到本地。筆者上傳了一份截至 20160920 最新 TProfiler-master.zip 到 CSDN 資源,讀者朋友也可以去這裏下載:http://download.csdn.net/detail/defonds/9635731

1.2. 安裝

SSH 登錄需要監控的遠程服務器主機,爲 TProfiler 新建安裝路徑:
mkdir /opt/tprofiler
本地將下載後的 TProfiler-master.zip 解壓縮,將 dist 目錄下的 profile.properties 以及 dist/lib 目錄下的 tprofiler-1.0.1.jar ftp 上傳到遠程服務器 /opt/tprofiler 目錄下。
最後將遠程服務器 /opt/tprofiler 目錄及其下所有文件的所有者改爲啓動 Weblogic 進程的用戶及其所在用戶組。

2. TProfiler 的配置部署

2.1. TProfiler 配置

編輯服務器 /opt/tprofiler/profile.properties 文件內容如下:
#log file name
logFileName = tprofiler.log
methodFileName = tmethod.log
samplerFileName = tsampler.log

#basic configuration items
startProfTime = 9:00:00
endProfTime = 23:00:00
eachProfUseTime = 5
eachProfIntervalTime = 50
samplerIntervalTime = 20
port = 30000
debugMode = false
needNanoTime = false
ignoreGetSetMethod = true

#file paths
logFilePath = ${user.home}/logs/${logFileName}
methodFilePath = ${user.home}/logs/${methodFileName}
samplerFilePath = ${user.home}/logs/${samplerFileName}

#include & excludes items
excludeClassLoader = org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader
includePackageStartsWith =com.caucho;com.defonds;com.fasterxml;com.sun.jersey;com.sun.jmx;
org.apache;org.codehaus;org.jdbcdslog;org.mybatis;org.quartz;org.springframework
excludePackageStartsWith = com.taobao.sketch;org.apache.velocity;com.alibaba;com.taobao.forest.domain.dataobject
紅色部分是我們修改後的內容,其它部分使用默認值。

2.2. Weblogic 啓動參數配置

在 Weblogic JVM 啓動參數里加入:
-javaagent:/opt/tprofiler/tprofiler-1.0.1.jar -Dprofile.properties=/opt/tprofiler/profile.properties
之後重啓 Weblogic。

3. TProfiler 的遠程操作

使用啓動 Weblogic 進程的用戶 SSH 遠程登錄正在進行壓測的機器。

3.1. 查看 TProfiler 當前狀態

java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 status
running
得到這個結果證明 TProfiler 正在進行採集工作。

3.2. 將 TProfiler 停止,以釋放其佔用的系統資源

隨時關閉 TProfiler:
java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 stop
java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 status
stop
隨時啓動以繼續採集:
java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 start
java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 status
running

3.3. 刷出數據

java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 flushmethod
會將數據刷出到 ~/logs/ 目錄下:
TProfiler的日誌.png

4. TProfiler 對性能方法的採集

4.1. 普通方法、線程統計

java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.analysis.SamplerLogAnalysis ~/logs/tsampler.log ~/logs/method.log ~/logs/thread.log

4.2. top 統計

java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.analysis.ProfilerLogAnalysis ~/logs/tprofiler.log ~/logs/tmethod.log ~/logs/topmethod.log ~/logs/topobject.log
方法執行時間統計:這個非常非常重要,這個是 TProfiler 最最重要的 feature,是其能夠傲視所有其他性能測試類(包括 jvm 性能測試類)軟件的關鍵所在,我們將會不止一次地在關鍵的時候受益於 TProfiler 的這一非常有用的特性。
上述命令刷出的 topmethod.log 部分結果如下:
com/defonds/core/ppts/common/support/JsonUtils:object2jsonString:123 13519 154 2083584
com/caucho/hessian/client/HessianURLConnection:sendRequest:156 15894 130 2072565
com/defonds/rest/core/client/proxy/ResourceJsonInvocationHandler:invoke:39 8123 113 921340
com/defonds/core/ppts/cache/service/impl/MerBankCfgServiceImpl:selectMerBankCfgByParams:72 54213 15 799322
com/defonds/core/ppts/incomes/biz/sinopay/service/impl/SinoPayBankReturnServiceImpl4Json:updateOrderSuccess:792 2495 176 438542
com/defonds/core/ppts/common/support/framework/bean/Message:<init>:76 6219 26 163741
com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer:serializeContents:107 51883 3 145556
com/defonds/core/ppts/cache/biz/cims/impl/AccountPrdAndBankCacheImpl:selectBasicProductCfg:144 16131 8 137029
格式說明:方法信息 執行次數 平均執行時間(單位:ms) 全部執行時間(單位:ms)

5. 性能方法的優化

根據 topmethod.log 統計結果,我們拿到了熱點方法 top10:

熱點方法 top10
方法名 被調用次數 平均執行時間(ms) 採樣內總執行時間(ms)
com/defonds/core/ppts/common/support/JsonUtils:object2jsonString:123 13519 154 2083584
com/caucho/hessian/client/HessianURLConnection:sendRequest:156 15894 130 2072565
com/defonds/rest/core/client/proxy/ResourceJsonInvocationHandler:invoke:39 8123 113 921340
com/defonds/core/ppts/cache/service/impl/MerBankCfgServiceImpl:selectMerBankCfgByParams:72 54213 15 799322
com/defonds/core/ppts/incomes/biz/sinopay/service/impl/SinoPayBankReturnServiceImpl4Json:updateOrderSuccess:792 2495 176 438542
com/defonds/core/ppts/common/support/framework/bean/Message:<init>:76 6219 26 163741
com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer:serializeContents:107 51883 3 145556
com/defonds/core/ppts/cache/biz/cims/impl/AccountPrdAndBankCacheImpl:selectBasicProductCfg:144 16131 8 137029
com/defonds/core/ppts/common/jms/retrieve/listener/DefaultMessageListener:handleMessage:64 2981 46 136180
com/fasterxml/jackson/databind/ser/BeanPropertyWriter:serializeAsField:573 53892 2 112553

這是壓測時根據多次採樣結果,揀選出的一次比較有代表性的一次。紅色部分值得我們去重點關注並優化一下,因爲極有可能就是應用瓶頸所在。這些代碼要麼是導致平均響應時間低下的一些點,要麼是導致大量臨時對象產生的一些點。
對於上篇博客中的結論,這些代碼的調優原則是:臨時對象能改成靜態對象進行復用就改成公用對象否則要想方設法縮短其生命週期;高頻訪問代碼提高響應速度。根據 jvm gc 日誌發現很多 young gc 之後堆內存已用空間不僅下降反而上升至最大使用量導致 full gc,臨時對象如果可以和其它線程複用的話改成靜態對象以減少大量線程 local 對象的產生。
以排名第一的熱點方法 com/defonds/core/ppts/common/support/JsonUtils:object2jsonString:123 爲例,看看如何來進行調優。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import org.codehaus.jackson.map.ObjectMapper; 

    public static <T> String object2jsonString(T t) { 

        try

            ObjectMapper objectMapper = instanceObjectMapper(); 

            return objectMapper.writeValueAsString(t); 

        } catch (JsonParseException e) { 

            log.error(e.getMessage(), e); 

            throw new SysException(e); 

        } catch (JsonMappingException e) { 

            log.error(e.getMessage(), e); 

            throw new SysException(e); 

        } catch (IOException e) { 

            log.error(e.getMessage(), e); 

            throw new SysException(e); 

        

    

    public static ObjectMapper instanceObjectMapper() { 

        JsonFactory jf = new JsonFactory(); 

        jf.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); 

        return new ObjectMapper(jf); 

    }

該熱點方法的優化建議:
這個方法平均調用時間在 154ms,如果在低併發時可能比這要小得多。但是高併發時可能要等待 GC 的堆內存釋放、GC 作業時對業務線程造成的暫停時間等因素影響,這個時間會被無限放大。

5.1. 臨時對象改成靜態對象

object2jsonString 方法的 objectMapper 對象,instanceObjectMapper 方法的 jf 對象;

5.2. json 處理由 jackson 改爲 fastjson

jackson 和 spring 整合的很好,提供的功能點很多很強大。但是其性能未必靠得住。
比如我們原來用過谷歌的 Gson 進行 json 處理,某個大對象的 json 解析使用 gson 是 100 多秒,而換成 fastjson 解析後是 900 多毫秒。上百倍的性能差距呀,這還是在單用戶操作、不能存在 CPU 和內存等資源限制及競爭的情況下拿到的數據。在此向貢獻出 fastjson 的阿里人致敬~

5.3. 頻繁 GC 的瓶頸已不復存在

針對 TProfiler 幫我們在海量業務代碼中定位到的 top5 性能代碼進行優化後,部署重新壓測,50 個用戶併發兩個小時左右,我們拉了幾次快照,上篇博客中定位的頻繁 GC 的性能瓶頸已不復存在,TRT 也由上篇博客優化到的 2.5 下降到 0.5,TPS 基本能穩定在 100 個。問題圓滿解決。

6. 需要注意的一些問題

6.1. TProfiler 端口號是否已被佔用

爲 TProfiler 選取端口號之前要先檢測一下該端口號是否已被佔用:
netstat -an | grep 30000

6.2. TProfiler 配置裏 includePackageStartsWith

一定要根據你自己的系統進行實際更改,不然就會遇到《TProfiler.log的內容爲空 #33》的問題,截圖如下:
TProfiler.log的內容爲空.png

6.3. 幾個命令配合使用

在壓測的時候,結合使用 start、stop、flushmethod、ProfilerLogAnalysis topmethod 等幾個命令,以拿到關鍵性的結果。如果能再結合 Weblogic、LoadRunner 的啓動、停止,效果最佳。不然的話,如果 JVM 已經跑了很多天,拿到的數據可能不是你想要的,反而會誤導你南轅北轍。

7. 後記

總體來講,TProfiler 配置部署、遠程操作、日誌閱讀都不太複雜,操作還是很簡單的。但是其卻是能夠起到一針見血、立竿見影的效果,幫我們解決了 GC 過於頻繁的性能瓶頸。
TProfiler 最重要的特性就是能夠統計出你指定時間段內 JVM 的 topmethod,這些 topmethod 極有可能就是造成你 JVM 性能瓶頸的元兇。這是其他大多數 JVM 調優工具所不具備的,包括 JRockit Mission Control。JRokit 首席開發者 Marcus Hirt 在其私人博客《Low Overhead Method Profiling with Java Mission Control》下的評論中曾明確指出 JRMC 並不支持 TOP 方法的統計:
JRMC並不支持TOP方法的統計.png
最後向具備開源精神的阿里技術團隊致敬~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章