以前在搞性能優化,發現有些經過Spring boot feign的RPC接口比較慢,達到100-200ms,按理RPC調用都是IP直連,不該該超過100ms的,而後使用阿里開源的arthas工具測試接口執行時間,結果發現同一個接口大概每10秒會出現一次慢100+ms,如圖:java
但實際上服務方的API層響應很快:算法
疑點:因爲我的開發機鏈接測試環境的註冊地址,有可能測試接口會調用我的地址,但測試機ip爲10.8,我的開發機爲192.168,但如果分到我的機器應該不止100ms。spring
操做:使用單獨的測試機依然重現api
結論:排除此緣由緩存
疑點: 1)懷疑線程keepalive過短,過時後從新開啓線程,增長延遲,查看代碼確實設置了keepalive=5,但開啓線程不該該100ms那麼多tomcat
2)懷疑配置出錯致使負載均衡算法延遲性能優化
操做:使用arthas測試執行時間,以下圖,能夠看出最慢是讀取流操做了session
結論:排除此緣由負載均衡
疑點: 既然不是調用者問題,那就從接收者身上找問題好了,從tomcat接收請求開始跟蹤,果真發現執行時間長的位置工具
這是什麼模塊呢?簡單來講就是加載jar中的靜態資源,接口請求過來以後須要判斷是否命中jar中的靜態資源。查看源碼發現每次加載完會緩存起來,但隔一段時間就從新加載,代表應該有地方清了緩存。搜索發現有個後臺線程ContainerBackgroundProcessor執行gc方法,方法內會將緩存設null
// ContainerBackgroundProcessor部分代碼 while(!ContainerBase.this.threadDone) { try { Thread.sleep((long)ContainerBase.this.backgroundProcessorDelay * 1000L); } catch (InterruptedException var8) { ; } if (!ContainerBase.this.threadDone) { // 用於重載standardContext、處理過時的session、清除jar中的靜態資源緩存等等 this.processChildren(ContainerBase.this); } } // AbstractArchiveResourceSet清除jar中的靜態資源緩存 public void gc() { Object var1 = this.archiveLock; synchronized(this.archiveLock) { if (this.archive != null && this.archiveUseCount == 0L) { try { this.archive.close(); } catch (IOException var4) { ; } this.archive = null; this.archiveEntries = null; } } }
因爲backgroundProcessorDelay默認爲10,因此每10秒清一次緩存,這也印證了現象中的狀況!注意ContainerBackgroundProcessor只清除是jar中的靜態資源,項目中的靜態資源並不會緩存於此,也就不會有影響了。
經過debug發現,每次加載的靜態資源出處是springfox-swagger-ui.jar(自動生成api文檔),裏面的確存在靜態資源,因而將其依賴去掉,發現延遲100ms的狀況也消失了!
結論:tomcat在啓動時,經過StaticResourceConfigurer.addResourceJars將全部jar中的靜態資源路徑保存到list中,請求過來時先今後list中加載所有資源並檢查是否命中,是則返回,若全部jar均沒有靜態資源則不會加載;爲了增長性能,在加載完靜態資源後會緩存下來,但設置了一個後臺線程,定時清除緩存(我的認爲:tomcat這樣作應該是爲了熱部署,但徹底沒有配置控制不gc的確有點沙雕,雖然耗時只有100+ms)。
最終結論:沒事不要把靜態資源扔到jar裏!!!
最終結論:沒事不要把靜態資源扔到jar裏!!!
最終結論:沒事不要把靜態資源扔到jar裏!!!