將一個介面響應時間從2s優化到 200ms以內的一個案例
一、背景
在開發聯調階段發現一個介面的響應時間特別長,經常超時,囧…
本文講講是如何定位到效能瓶頸以及修改的思路,將該介面從 2 s 左右優化到 200ms 以內 。
二、步驟
2.1 定位
定位效能瓶頸有兩個思路,一個是通過工具去監控,一個是通過經驗去猜想。
2.1.1 工具監控
就工具而言,推薦使用 arthas ,用到的是 trace 命令
具體安裝步驟很簡單,大家自行研究。
我的使用步驟是,先最終待研究的函式的最外層:
trace com.xxx.service.impl.AServiceImpl refresh
其中耗時最多的子函式會被標紅色
Affect(class-cnt:2 , method-cnt:2) cost in 525 ms.
`---ts=2020-0X-0Y 13:33:18;thread_name=DubboServerHandler-127.0.0.1:20880-thread-36;id=24e;is_daemon=true;priority=5;TCCL=com.mmm.WWWClassLoader@4362d7df
`---[1761.834357ms] com.xxx.service.impl.AServiceImpl$$EnhancerBySpringCGLIB$$e3cd7543:refresh()
+---[0.017066ms] com.xxx.service.impl.AServiceImpl$$EnhancerBySpringCGLIB$$e3cd7543:$jacocoInit()
`---[1761.00347ms] org.springframework.cglib.proxy.MethodInterceptor:intercept()
`---[1757.647111ms] com.xxx.service.impl.AdServiceImpl:refresh()
+---[0.006629ms] com.xxx.biz.yyy.service.impl.AServiceImpl:$jacocoInit()
+---[0.004073ms] java.util.Collections:singletonList()
+---[1709.203302ms] com.yyy.service.impl.AServiceImpl:refreshSomeThings()
`---[48.135719ms] com.yzzzz.service.impl.AServiceImpl:createSurvey()
繼續再 trace 耗時最多的子函式。
trace com.yyy.service.impl.AServiceImpl refreshSomeThings
最終定位到最影響耗時的函式上,繼續往下跟。
最後發現造成效能瓶頸的函式是一個網路請求,單次請求大概 100多毫秒。
為了避免呼叫的資料量太大,專案中採用分批呼叫的方式,但是每個批次太小,導致請求次數過多。
假設請求 N 次(如 10次),每次請求 M毫秒(如 200ms),總耗時就是 N*M (2000)毫秒。
2.1.2 猜想
如果開發經驗足夠豐富,大致可以猜出哪些介面可能存在效能問題。
最常見的有:
- 慢 SQL 會是效能瓶頸,主要原因是沒有命中索引。
- 傳送遠端資料請求(RPC 遠端呼叫、HTTP 遠端呼叫)。
- I/O 操作等。
最常見的是在迴圈中執行 SQL或者網路請求。
然後審查一下自己的程式碼發現 SQL 查詢部分都可以命中索引,呼叫鏈路上有一個函式最終會呼叫 HTTP 請求,而且是在一個迴圈裡。
因此最有可能成為造成介面延時的是底層依賴的 HTTP 請求。
2.2 解決
既然 HTTP 請求是效能瓶頸,那麼要儘量減少請求,或者讓請求由序列改為多執行緒併發/並行。
減少網路請求的次數,可以將多個請求合併成一個批量介面(或者增加批量請求的每個批次的大小)。
這裡的批次甚至可以使用動態配置,根據情況動態修改。
將序列改為並行可以使用 CompletableFuture
來實現,具體參見:《Java 資料分批呼叫介面的正確姿勢》
最終一個介面從1 s - 2 s降低到了 200 ms 以內。
3、總結
很多人不願意學習 arthas ,如果不去學習不去了解,遇到可以用上的場景想不起來去用。
另外大家可以積累下開發過程中常見的效能瓶頸的原因,以便未來遇到效能瓶頸是可以快速排查和解決問題。
最後大家在開發階段或測試階段,多看錯誤日誌,多關注介面的響應時長等,儘早排除問題,儘早做優化。
希望本文對大家開發能夠有幫助。
如果我的文章對你有幫助,歡迎關注,點贊評論!!
相關文章
- 【MySQL】NOT EXISTS優化的一個案例MySql優化
- JS判定一個給定的時間在某個時間範圍內JS
- 如何快速從 0 到 1 構建起一個公司的自動化介面
- 寫一個格式化時間的方法
- java 一個類實現兩個介面的案例Java
- JS判定一個給定的時間區間在哪些時間段範圍內JS
- 從零寫一個時間序列資料庫資料庫
- 記一次介面效能優化實踐總結:優化介面效能的八個建議優化
- 一個人的漢化:從《UnderRail》到《地鐵餘生》AI
- 將一個陣列複製到另一個陣列上陣列
- 我是如何將頁面載入時間從6S降到2S的?
- 探討把一個元素從它所在的div 拖動到另一個div內的實現方法
- 從0到1打造一個 WebRTC 應用Web
- kubernetes部署第一個應用案例
- Rust中如何將本地時間轉換為另一個時區?Rust
- 從2s最佳化到0.1s
- 最簡單的服務響應時長優化方法,沒有之一優化
- 關於MySql 設定一個間隔時間 執行一個事件MySql事件
- 一個網頁從輸入地址回車,到完整展示網頁內容這段時間裡,做了哪些工作網頁
- 記一次 500併發,平均響應時間慢-調優過程~~
- 瞧瞧別人家的API介面,那叫一個優雅API
- Oracle效能優化-SQL優化(案例一)Oracle優化SQL
- MySQL SQL優化案例(一)MySql優化
- 將一個Activity中的資料傳到另一個Activity的Fragment中的方法Fragment
- 分享一個查詢某個使用者過去一段時間內執行的SQL語句。SQL
- 從需求到實施整流程及相關文件模板(附一個案例)
- 自己實現一個VUE響應式--VUE響應式原理Vue
- 從一個罕見案例聊聊我對社群的看法
- 內卷又擺爛的時代,如何培養一個優秀的孩子?
- 這些遊戲“懷胎”時間堪比哪吒,預約時間一個比一個長遊戲
- mysql 從一個表中查詢,插入到另一個表中MySql
- Oracle效能優化方法論的發展之三:基於響應時間分析的效能優化方法論Oracle優化
- 從0到1實現一個模組間通訊的服務元件元件
- [Unity]記一個倒數計時介面Unity
- Solon 框架如何方便獲取每個請求的響應時間?框架
- 上傳一個簡單的應用程式並在 4 時間內獲利有多困難?
- 記一個效能優化問題優化
- 淺談App響應時間最佳化APP