背景
項(xiàng)目中使用AWS的SQS消息隊(duì)列進(jìn)行異步處理,QA通過壓測發(fā)現(xiàn)單機(jī)TPS在23左右,目標(biāo)性能在500TPS,所以需要對消費(fèi)邏輯進(jìn)行優(yōu)化,提升消費(fèi)速度。
目標(biāo)
消費(fèi)TPS從23提升到500
優(yōu)化流程
優(yōu)化的思路是先分析定位性能瓶頸,再針瓶頸進(jìn)行優(yōu)化。
性能定位
要定位性能,先要準(zhǔn)確評估每秒處理的消費(fèi)數(shù)量,以及處理每個(gè)消息過程中,每一步操作的耗時(shí),發(fā)現(xiàn)耗時(shí)大頭在哪里。
準(zhǔn)確評估消費(fèi)速度(TPS)
消費(fèi)消息的入口是AwsConsumer#doUpdateCoin,所以可以通過Arthas的monitor命令監(jiān)控方法的執(zhí)行TPS和RT。
>?monitor -c 1?AwsConsumer doUpdateCoin -n 1000
這個(gè)命令會統(tǒng)計(jì)doUpdateCoin的調(diào)用信息,每1秒打印一次結(jié)果,總共打印1000次。通過它能定量分析消費(fèi)的TPS,命令會返回以下信息。
監(jiān)控項(xiàng) |
說明 |
---|---|
timestamp | 時(shí)間戳 |
class | Java 類 |
method | 方法(構(gòu)造方法、普通方法) |
total | 調(diào)用次數(shù) |
success | 成功次數(shù) |
fail | 失敗次數(shù) |
rt | 平均 RT |
fail-rate | 失敗率 |
這是一次調(diào)用的結(jié)果:
可以看到方法每秒執(zhí)行26次,平均執(zhí)行時(shí)間是179.44秒。從這里我們能得出兩個(gè)結(jié)論:
- TPS是26,的確不高
- AVT-RT在179.44ms,那么一個(gè)線程TPS約等于5。
因?yàn)镽T比較高,猜測在RT上還有優(yōu)化的空間,下面從每條消息消費(fèi)的過程,繼續(xù)看是否存在瓶頸。
查看每次處理的明細(xì)
要看每次請求的信息,可以通過tt命令,它會采集方法每次執(zhí)行的耗時(shí)、成功還是失敗。
>?tt -t?AwsConsumer doUpdateCoin -n 1000
表格字段 |
字段解釋 |
---|---|
INDEX | 時(shí)間片段記錄編號,每一個(gè)編號代表著一次調(diào)用,后續(xù) tt 還有很多命令都是基于此編號指定記錄操作,非常重要。 |
TIMESTAMP | 方法執(zhí)行的本機(jī)時(shí)間,記錄了這個(gè)時(shí)間片段所發(fā)生的本機(jī)時(shí)間 |
COST(ms) | 方法執(zhí)行的耗時(shí) |
IS-RET | 方法是否以正常返回的形式結(jié)束 |
IS-EXP | 方法是否以拋異常的形式結(jié)束 |
OBJECT | 執(zhí)行對象的hashCode() ,注意,曾經(jīng)有人誤認(rèn)為是對象在 JVM 中的內(nèi)存地址,但很遺憾他不是。但他能幫助你簡單的標(biāo)記當(dāng)前執(zhí)行方法的類實(shí)體 |
CLASS | 執(zhí)行的類名 |
METHOD | 執(zhí)行的方法名 |
這是一次調(diào)用的結(jié)果:
從這里可以看出,消息處理耗時(shí)有的大,有的小,說明處理性能不穩(wěn)定。需要再深入看RT較大的消息耗時(shí)在哪里。
處理一條消息的內(nèi)部耗時(shí)
要看單次處理過程中,每個(gè)步驟的耗時(shí),一般我們會通過在代碼前后記錄時(shí)間,再用日志打印出來。例如:long?s?=?System.currentTimeMillis();
這種方式效率很低,需要不斷加日志,并重新部署服務(wù)。Arthas有一個(gè)trace命令,可以查看方法的調(diào)用棧信息,包括調(diào)用的方法和方法執(zhí)行的耗時(shí)。
>?trace?AwsConsumer doUpdateCoin '#cost > 100' -n 1
這是一次調(diào)用的結(jié)果:
?文章來源地址http://www.zghlxwxcb.cn/news/detail-678592.html
這個(gè)命令會打印doUpdateCoin耗時(shí)大于100ms的請求調(diào)用棧信息,可以看到doUpdateCoin方法執(zhí)行了323ms,其中99.62%的耗時(shí)集中在PlayerService:loadByOpenId()方法調(diào)用。然后我們就會想看一下loadByOpenId方法到底什么地方耗時(shí)。
trace命令不能直接指定調(diào)用棧的層級,可以通過動態(tài)trace的方式,再創(chuàng)建一個(gè)listener去監(jiān)聽loadByOpenId方法,這樣會把第二個(gè)listener的結(jié)果打印在前面的trace結(jié)果上。
> trace PlayerService loadByOpenId --listenerId 9
?
可以看到,在原來的結(jié)果上多了loadByOpenId方法調(diào)用的明細(xì)。也發(fā)現(xiàn)了loadByOpenId方法耗時(shí)集中在load方法上,這是ORM框架提供的方法,直接去查詢數(shù)據(jù)庫。所以基本可以斷定,本次處理慢是由于這個(gè)查詢引起的。后面就是看查詢條件是沒有命中索引導(dǎo)致了慢,還是數(shù)據(jù)庫本身性能存在問題。
總結(jié)
因?yàn)楸敬螇簻y是在測試數(shù)據(jù)庫,所以數(shù)據(jù)庫本身不穩(wěn)定,雖然定位到了這個(gè)性能瓶頸,對消費(fèi)邏輯優(yōu)化幫助不大,需要更精準(zhǔn)的評估線上數(shù)據(jù)庫的性能。但是通過monitor命令長時(shí)間觀察doUpdateCoin方法的執(zhí)行情況,發(fā)現(xiàn)大部分時(shí)間平均RT其實(shí)是比較低的,所以不應(yīng)該是單次請求慢而降低了總體的消費(fèi)TPS??赡苁且?yàn)镾QS消息拉取階段存在瓶頸,所以嘗試加大了消費(fèi)的線程數(shù)、將單條拉取改成批量拉取。重新壓測后,消費(fèi)TPS從23提升到了342。
?文章來源:http://www.zghlxwxcb.cn/news/detail-678592.html
?
到了這里,關(guān)于Arthas協(xié)助MQ消費(fèi)性能優(yōu)化的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!