性能壓測詭異的Requests/second 響應刺尖問題

最近一段時間都在忙着轉java項目最後的衝刺,前期的coding翻代碼、debug、fixbug都逐漸收尾,進入上線前的性能壓測。

雖然不是大促前的性能壓測要求,但是爲了安全起見,需要摸個底心裏有個數。

畢竟這次轉java的服務都是集團核心公共服務(主要是訂單域服務)。(等我們順利上線了,我再來好好總結下其中的坎坷和壯舉。)

廢話不多說了,直接進入主題。

由於這次壓測主要重點是關注正向的兩個核心訂單服務,下單服務、查單服務。查單服務初步壓測下來問題不大,主要是db的索引和cache的問題。

下單服務有兩個核心接口,預訂單查詢、創建訂單。預訂單查詢主要是訂單的前置狀態的結算頁彙總計算(不僅是結算頁),不落具體訂單,如,各種促銷、卡券碼、虛擬幣的規則計算等等。

創建訂單邏輯稍微複雜點,對周邊的系統及中間件依賴也比較多,所以需要重點關注,至少心中要有數,哪怕下游的哪個服務的性能有問題,在下次大促的時候可以優化掉。

(並不是說所有性能問題都需要及時優化,只要保證能撐得起業務量的一定範圍就好,因爲性能優化無止境,需要把握好節奏。)

提交橫向壓測前我們需要自己先過一遍,這樣才能加快壓測的效率,由於時間比較緊再加上客觀的環境問題,我將服務中幾個沒有壓測環境的依賴去掉。(有關壓測的一些實踐我將在下篇文章好好總結下,這裏就不展開了。)壓測了幾輪(時間差不多30分鐘左右。),消除了一些環境、代碼、依賴的障礙,提交橫向走壓測流程,接着就去忙其他的事情了。(詭異的問題比較多~_~,mybatis pagehelperplugin好像也有點併發問題,還沒定位到,不知道是用的不對還是什麼情況,繼續排查,有結論了我在總結分享下。)

1.壓測報告:

wKiom1nGBYqxf0onAABLiZL8Rk0019.png-wh_50

wKiom1nGBbTACJLMAAAkBZP4sl4049.png-wh_50

wKioL1nGBY6Qa1BTAACka5EGMTg939.png-wh_50

併發用戶數沒變化,平均響應時間沒變化,但是request/second奇怪了。我相信大多數開發的直覺就是fullgc了,我也一樣。

立馬去看下服務器的GC監控,同時看下程序的GCer配置是CMS。(CMS主要解決低延遲問題《深入理解JAVA虛擬機》)

2.查看服務器監控情況:

JAVA GC:

wKiom1nGBm6xQF9fAABiGLijIrA612.png-wh_50

沒發現fullgc,再看下幾個其他的系統資源是否有異樣。

cpu:

wKioL1nGBlay8f6TAABwHl5uCIk628.png-wh_50


刺尖的幾個點CPU idle 基本都是100%,us也是0%,非常奇怪。再看下其他的資源。

network:

wKioL1nGBnuSW7FnAACPoZmc30c073.png-wh_50

也是比較奇怪的,receive到是挺正常的,send基本爲0了,感覺像是某個調用或者發送停止了,能接受請求,但是對下游的調用貌似停止了。

memory:

wKiom1nGBs-j49jEAABkxiizo14320.png-wh_50


內存咋一看好像有點問題,但是瞭解linux 內存計算方式和使用原理都知道這其實問題不大。(下篇文章中會具體講解關於壓測的時候各個指標如何查看和計算,在壓測時候重點關注top中的swap區。)

我程序裏面基本上沒有用到什麼大量的磁盤操作,基本上就一個日誌輸出,別的沒有了。(linux cache區不管是讀還是寫都會被cache住,會在cache裏維護一個邏輯地址空間。我將在下篇文章中演示出來,每當我刪除磁盤的日誌文件,cache區都會瞬間釋放。https://www.ibm.com/developerworks/cn/linux/l-cache/index.html

重點是關注下JAVAGC 容量:(java程序的內存分配由“內存分配器+GC完成”《java性能優化權威指南》)

wKiom1nGBuihksuDAACqgRZt7SM116.png-wh_50

這是壓測的下單服務機器資源情況。

3.查看DB情況:

wKioL1nGBsagkGaLAACPAkylBeY214.png-wh_50

wKiom1nGBxTx216UAAB0BlJb0V8273.png-wh_50

wKioL1nGBtuBQBCXAABim1H_CJ8043.png-wh_50

wKioL1nGBtuTl8wVAACNWw05-7g240.png-wh_50

看了下DB情況,也沒啥異樣,都是在相同的時間點,一下子負載沒有了,時間都能對上。網絡、磁盤、CPU都沒有活動。

4.分析

上面圖中有一幅圖有點問題,不知道大家看出來了沒有。就是我下單服務的應用服務器的網絡流量有問題,receive、send對不上。

wKioL1nGBxmTRYsjAACPoZmc30c674.png-wh_50


我們分析下,receive、send不配對意味着什麼,我們有200併發,延遲1秒啓動,基本上跑上個十幾分鍾,你能大概想象出200併發的請求空間路線圖麼。其實它會呈現出每秒鐘都有請求進來,這是壓力機的請求,每秒也會有請求出去,去訪問它所依賴的服務或者中間件。如果,我們設想從壓力機爲開始點,把請求和響應想象成一個圓,那在圓的任何一個角度上都有請求和響應。

我們注意看下,DB的網絡流量圖,它就是比較正常的,沒有請求沒有發送。而應用服務器有點說不通,只有進來的沒有出去的,這段時間內到底在幹嘛,而且分佈很平均。

5.排查

其實這個時候有一個結論,就是服務器其實沒有瓶頸,不管是應用服務器還是DB、cache。那問題應該是在程序方面。(性能分析由上至下、由下至上集合分析《java性能優化權威指南》)。

開始嘗試排查依賴服務,下單服務主要依賴商品、促銷。cache不是問題,因爲本地有一級緩存,而且緩存的過期時間對不上,壓測環境的redis和MySQL在一臺機器上。所以DB沒有問題,基本上redis應該也沒啥問題。(這臺機器很強悍)還有部分的依賴業務方的接口我已經註釋掉了,不會有依賴。

開始懷疑商品、促銷,但是我之前分別對這兩個服務進行過壓測,這兩個服務基本上都是命中cache,QPS基本上接近18000。現在也只好對這兩個服務再進行一輪詳細的壓測。

結果很遺憾,沒啥線索,性能很好。

開始排查線程池問題,是否有block線程,通過jstack 打印出線程,基本上都是XNIO的condition wait,也沒有啥不正常。因爲下單服務的其他接口都挺正常的,線程池問題應該不大。下單成功之後有意個hold的場景,就是hold虛擬幣、卡券碼等等之類的邏輯,這裏面使用了fiexd線程池(5個,設置了飽和策略及日誌輸出。),問題也不大。

開始排查日誌,restful-slow.log,jdbc-slow.log、錯誤日誌等等,一頓cat… grep…wc –l,啥也沒有異常。(shit開始冒汗了。。。)

只能上大招了,開始嘗試注代碼,然後壓測,逐個嘗試,先註釋DB、然後線程池hold邏輯、然後發送消息。(無賴之舉。。。)

6.浮出水面

等我嘗試註釋掉髮送消息的邏輯時候發現問題不出現了,有希望了。開始進去看代碼,沒啥邏輯,走的是spring 的RabbitTemplate.convertAndSend 方法。(這是個同步方法,沒有任何聲明說他是async的。)

/**發送消息*/    
template.convertAndSend(messageConfig.getExchangeName(), routingKey, message, amqpMessage -> {

翻了下資料,沒啥特殊的使用要求。

順便看了下配置文件,發送消息走的是qa環境,這個我知道,因爲當時壓測環境的rabbitmq一時還沒好,而且我們走的是先定義再使用queue的流程,所以如果要用我需要先上去配置好才能使用。當時圖省事就先用了,自己壓測下來也沒啥問題,畢竟MQ的設計吞吐量都很高的,TPS足夠我們用的,再加上我之前也壓過qa的MQ沒啥問題。

(資源沒隔離是因爲一些客觀原因,有時候壓測環境是臨時搭建的。用到qa環境的中間件還有codis,但是codis基本是二級緩存,所以問題不大,先過。(回頭沒轍再來找它。)

搞來了qa環境的rabbitmq服務器賬號,同時打開rabbtimq管理界面中的dashboard。開始重點關注這臺服務器。(top命名打開,P\M看下rabbitmq各項指標。)

wKiom1nGB3Pjk_9TAAA7as9u7tw960.png-wh_50

問題一如既往的出現了(我已經能接受了~_~,它要是不出現我纔想死尼,已經來回折騰很久了。)很好,rabbtimq dashboard也出現刺尖了。

wKioL1nGB2GTAmAuAABPPXl-Og4807.png-wh_50

現在基本上是rabbtimq服務器的性能問題了,可能你會覺得問題找到了。但是我還是無解,爲什麼出現這個問題,爲什麼時間這麼規律,肯定有蹊蹺,繼續排查,到底是rabbtimq服務器的CPU問題還是disk問題,還是network問題。這次重點看下top。

wKiom1nGB8DxOV3FAABT5rV38vg934.png-wh_50

wKioL1nGB4jQXAPWAABI6n5Yzc8762.png-wh_50

wKiom1nGB8HhdJscAAA3HYWYFPs919.png-wh_50

wKioL1nGB4jDqOsVAABYnDBIhxM201.png-wh_50

同樣問題的出現CPU不正常,而且wait 率比較高。是不是可以這樣推理,wait率高了,導致大量線程(子進程)掛起,所以看起來CPU利用率佔的就高,也說的通。(先這麼假設,來驗證它就知道了)

有一點我可以肯定,根據rabbtimq推送消息原理,一個消息必須發送給所有監聽的queue,這些queue必須落盤纔算這次publish成功,纔會返回。(可以參考《Rabbitmq in Action》)

這一點可以通過rabbtimq queue的dashboard 中的publish in 和 publish out才查看,publish out 是publish in * queue的數量。

也就是說發送消息不是發送給exchange就結束了,我們配置的是topic模式,這個消息類型後面有4個queue,同時這幾個queue都有消費者在獲取消費消息。但是由於獲取消息的方式是pull模式,也不會存在多大的併發獲取消息的情況。而且這些queue裏面的消息都非常多,當我不壓的時候CPU也不高,pull消息的開銷對服務器來說network多點,CPU不會太多。

上圖中的cpu wait率有點不正常是因爲exchange同步寫4個queue且落盤,所以有這個問題。

基於這個推理,我考慮用一個空exchange來接受消息,根據原理指導,exchange收到消息之後如果發現沒有任何queue可以投遞就直接丟棄了。

壓測下來一切正常,沒有出現刺尖情況(真爽~_~),cpuwait 正常0。基本上定位到問題了。是因爲rabbtimq本身的負載不夠了,性能跟不上所以導致這個問題,這也算加深了rabbtimq的部分原理。

在基於這個推理,我用了一個不持久化的queue來接受消息,也就是說這個消息是不會持久化的,cpuwait應該是0。

壓測下來一切正常,cpuwait爲0(心情無比的順暢)。

提交橫向第二輪壓測。

7.打臉

等我在開會的時候,壓測兄弟找我,哥哥那個問題又出現了。

wKiom1nGB-Dg4TogAACIxmkv0XA888.png-wh_50

(我一時矇蔽,我擦什麼情況。)調整了下,仔細看了下那個刺尖的出現的時間比以前長了。原來大概十五分鐘,現在要半小時。由於qa環境機器沒有安裝壓測監控工具,不知道那段時間裏發生了什麼。(壓測執行時間1小時)

我之前都是30分鐘,我嘗試用空exchange壓了一小時(已是週五晚24:00點左右,洗澡睡覺,明早上看結果)。

早上起來看沒出現那個問題。爲什麼我用不持久的queue還有問題,而且這個queue是沒有任何consumer的,這已經涉及到rabbtimq的底層原理了。rabbtimq用的是erlang語言寫的,看源碼一時半會估計路都找不到。還是想其他辦法。

又嘗試用持久化queue來壓測一把,看下到時候啥情況,仔細盯着rabbtimq dashboard,果然又出現了。(計算機問題永遠不存在巧合,不確定。)

wKiom1nGB_PxQ031AAB7LYk_JkU911.png-wh_50

wKioL1nGB9WS6BbnAABTqNCJDdE550.png-wh_50

wKiom1nGCA6g7KBdAAB3DlqOXXw401.png-wh_50wKioL1nGB9XgII2JAACCGijBsbE199.png-wh_50


注意看下圖中的In memory,shit原來落盤了,哪怕你設置不持久化爲了內存利用率,它會將消息落盤,注意看Persistent沒有任何消息。消息總量1.1G,內存中只有119MB。

爲什麼會有那麼大的disk write。由於大量的磁盤寫入,導致publish消息的時候block了。具體爲什麼會這樣就要去研究rabbtimq源碼了。這些在rabbtimq的配置中應該有策略的,由於不是太熟悉rabbtimq,所以這裏就只好先告一段落了。

8.總結

能隔離環境的儘量隔離,排查環境問題最頭疼,但是有時候又無法避免。(下篇壓測文章分享下,環境問題的排查方式和工具)

遇到問題一定要搞清楚根源,就算找不到根源也知道把它限定在某個範圍內,比如限制到DB、操作系統等等


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