Spring boot性能優(yōu)化

? ? ? 筆者剛?cè)肼毿鹿绢I(lǐng)導(dǎo)讓針對(duì)api項(xiàng)目進(jìn)行重構(gòu)缚甩,由于當(dāng)前系統(tǒng)用play框架寫的加上歷史遺留原因薇搁,造成當(dāng)前的api項(xiàng)目難以維護(hù)以及部署粤铭。重構(gòu)便成了迫在眉睫的事畏铆。由于公司的業(yè)務(wù)性質(zhì)双泪,要求單臺(tái)機(jī)器api的吞吐量很高持搜,大家都知道springboot的好處,可以快速搭建起web服務(wù)焙矛。所以在選型時(shí)筆者只是寫了個(gè)簡(jiǎn)單的接口然后用ab命令對(duì)這個(gè)接口進(jìn)行了性能壓測(cè)葫盼。因?yàn)楣P者認(rèn)為吞吐量問(wèn)題springboot可以完全勝任。沒(méi)有過(guò)多的考慮性能不達(dá)標(biāo)的問(wèn)題村斟。

? ? ? 于是筆者便開開心心的按照老系統(tǒng)的邏輯進(jìn)行重構(gòu)贫导。根據(jù)需求接口返回類型需要根據(jù)請(qǐng)求后綴是json還是xml提供相應(yīng)的返回?cái)?shù)據(jù)格式。其他后綴結(jié)尾的或者沒(méi)有后綴的返回錯(cuò)誤碼蟆盹。筆者當(dāng)時(shí)想到兩種方案脱盲。一種是直接在@RequestMapping注解中通過(guò)value設(shè)置支持的后綴格式。如:@RequestMapping(value = {"/ping.json", "/ping.xml"}, method = RequestMethod.GET)日缨。另一種是在@RequestMapping中不設(shè)置后綴如圖一钱反。通過(guò)實(shí)現(xiàn)WebMvcConfigurer配置類。實(shí)現(xiàn)configurePathMatch方法開啟后綴匹配匣距。實(shí)現(xiàn)configureContentNegotiation方法根據(jù)后綴進(jìn)行返回格式設(shè)置如圖二面哥。然后再寫個(gè)攔截器對(duì)非json和xml結(jié)尾的請(qǐng)求進(jìn)行攔截如圖三。為了簡(jiǎn)單少寫代碼毅待。筆者選擇了第二種方式實(shí)現(xiàn)尚卫。然后就開啟了擼代碼的模式。在完成所有開發(fā)任務(wù)尸红,進(jìn)入測(cè)試階段時(shí)吱涉。測(cè)試小朋友跑過(guò)來(lái)跟我說(shuō):少年你重構(gòu)的api性能不達(dá)標(biāo)。現(xiàn)有的2核4G單機(jī)QPS能達(dá)到2000外里。你重構(gòu)的只能達(dá)到七八百怎爵。當(dāng)時(shí)內(nèi)心數(shù)萬(wàn)個(gè)草泥馬在奔騰。

圖一
圖二
圖三

? ? ? 沒(méi)辦法各種百度尋找優(yōu)化方案盅蝗。試過(guò)換各種web容器鳖链。由tomcat換到j(luò)etty再到undertow。試過(guò)配置各種參數(shù)墩莫。然而并沒(méi)有什么提升芙委。看到一篇文章說(shuō)可以使用異步請(qǐng)求如圖四狂秦。先釋放容器分配給請(qǐng)求的線程與相關(guān)資源灌侣,減輕系統(tǒng)負(fù)擔(dān),釋放了容器所分配線程的請(qǐng)求裂问,其響應(yīng)將被延后侧啼,可以在耗時(shí)處理完成時(shí)再對(duì)客戶端進(jìn)行響應(yīng)玖姑。頓時(shí)喜出望外,以為找到了解決的辦法慨菱。然而并沒(méi)有什么卵用焰络。一度懷疑最初的選型是錯(cuò)誤的。但是我想springboot的性能應(yīng)該不能這么不堪吧符喝。于是便開始查找自己的代碼闪彼。跟蹤線程耗時(shí)方法。

圖四

? ? ? 有過(guò)性能調(diào)優(yōu)的同學(xué)應(yīng)該都熟悉 jvisualvm协饲,jdk自帶監(jiān)控程序畏腕。可以監(jiān)控本地或遠(yuǎn)端cpu茉稠、內(nèi)存描馅、線程等實(shí)時(shí)動(dòng)態(tài)信息。以及對(duì)線程進(jìn)行快照而线。對(duì)線程內(nèi)方法調(diào)用耗時(shí)統(tǒng)計(jì)等功能铭污。非常強(qiáng)大。筆者用的是undertow做為web容器膀篮∴谀可以看到圖五、圖六它有跟netty類似的IO模型誓竿,IO線程負(fù)責(zé)接收請(qǐng)求磅网,然后把請(qǐng)求放到任務(wù)池中,由后面的任務(wù)線程進(jìn)行處理筷屡。這也解釋了為什么我之前用異步請(qǐng)求沒(méi)有提升性能的原因涧偷。因?yàn)楸旧韚ndertow已經(jīng)是異步的了。自己再進(jìn)行異步操作毫無(wú)意義毙死。tomcat也是同樣的道理燎潮。tomcat7以上默認(rèn)支持NIO,所以自己再實(shí)現(xiàn)異步請(qǐng)求操作沒(méi)有什么意義规哲。

圖五
圖六

? ? 然后我用wrk命令進(jìn)行壓測(cè)跟啤,看下任務(wù)線程中哪些操作是比較耗時(shí)的诽表,wrk -t 10 -c 500 -d 15s --latency -s http://127.0.0.1:2551/ping.json唉锌。10個(gè)線程500個(gè)連接,持續(xù)15秒竿奏“兰颍可以看到?jīng)]有任何業(yè)務(wù)邏輯的接口QPS只有1715。對(duì)任務(wù)線程抽樣進(jìn)行快照如圖八泛啸。展開其中一個(gè)線程任務(wù)圖九绿语。查看耗時(shí)的調(diào)用方法。如圖十中DispatcherServlet在調(diào)用doDispatch方法占用了64.2%的時(shí)間。一個(gè)doDispatch怎么會(huì)用這么多的時(shí)間呢吕粹?繼續(xù)追蹤方法內(nèi)調(diào)用getHander种柑,最后耗時(shí)在getMatchingCondition中。

圖七
圖八
圖九
圖十

查看源碼從doDispatch開始跟蹤匹耕,發(fā)現(xiàn)當(dāng)程序啟動(dòng)時(shí)會(huì)把@RequestMapping注解的path放到map集合中聚请,當(dāng)有請(qǐng)求時(shí),先去map中獲取對(duì)應(yīng)的路徑稳其,如果有則返回方法驶赏,沒(méi)有則根據(jù)設(shè)置的后綴匹配規(guī)則進(jìn)行遍歷匹配圖十三。其中畫框的屬性是不是很熟悉既鞠。對(duì)煤傍,它就是實(shí)現(xiàn)WebMvcConfigurer時(shí)設(shè)置的配置。? 如寫的是@RequestMapping(value = {"/ping"}, method = {RequestMethod.GET}) 嘱蛋,但請(qǐng)求的是/ping.json蚯姆,第一次查找在集合中沒(méi)有以/ping.json為path的方法,就會(huì)遍歷所有路徑集合進(jìn)行拆分后綴匹配洒敏。直到匹配到為止蒋失。筆者的項(xiàng)目中有300個(gè)接口,500多個(gè)路徑桐玻。如果不顯示的給出后綴篙挽,每次請(qǐng)求都會(huì)遍歷一遍這500多個(gè)路徑,造成耗時(shí)镊靴。

圖十一
圖十二
圖十三

? ? ? 最后猜想是匹配路徑耗時(shí)導(dǎo)致吞吐量變低铣卡。于是把注解中路徑后綴顯示給出@RequestMapping(value = {"/ping.json", "/ping.xml"}, method = {RequestMethod.GET}) , 再進(jìn)行一次壓測(cè)。結(jié)果QPS為9384偏竟,翻了4倍多煮落。到此為止才算把性能提升上來(lái)。符合上線標(biāo)準(zhǔn)踊谋。

圖十四

? ? ? 此次調(diào)優(yōu)過(guò)程中發(fā)現(xiàn)還有好多需要優(yōu)化的地方蝉仇,比如日志,集成的swagger殖蚕,actuator等等轿衔。都多少影響性能。但為了增加必要功能睦疫,損失些性能也是可以接受的害驹,有些不必要的損失性能還是要找到根源解決掉,筆者遇到的情況未必適合所有人蛤育。不過(guò)可以給那些想提升性能的朋友提供一些思路宛官。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末葫松,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子底洗,更是在濱河造成了極大的恐慌腋么,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亥揖,死亡現(xiàn)場(chǎng)離奇詭異党晋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)徐块,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門未玻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人胡控,你說(shuō)我怎么就攤上這事扳剿。” “怎么了昼激?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵庇绽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我橙困,道長(zhǎng)瞧掺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任凡傅,我火速辦了婚禮辟狈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夏跷。我一直安慰自己哼转,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布槽华。 她就那樣靜靜地躺著壹蔓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猫态。 梳的紋絲不亂的頭發(fā)上佣蓉,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音亲雪,去河邊找鬼勇凭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匆光,可吹牛的內(nèi)容都是我干的套像。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼终息,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼夺巩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起周崭,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柳譬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后续镇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體美澳,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年摸航,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了制跟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酱虎,死狀恐怖雨膨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情读串,我是刑警寧澤聊记,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站恢暖,受9級(jí)特大地震影響排监,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杰捂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一舆床、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嫁佳,春花似錦峭弟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至熄浓,卻和暖如春情臭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赌蔑。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工俯在, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人娃惯。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓跷乐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親趾浅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子愕提,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355