摘要:本文將以Sermant的SpringBoot 注冊插件的性能測驗及優化程序為例,分享在Java Agent場景如何進行更好的性能測驗優化及在Java Agent下需要著重注意的性能陷阱,
作者:欒文飛 高級軟體工程師
一、背景介紹
Sermant是一個主打服務治理領域的Java Agent框架,在服務治理中難免會有針對業務流量進行決議和處理的程序,此類服務治理能力將會對微服務的服務能力產生一定的性能影響,作為一個基于Java Agent技術做服務治理的框架,我們需要在保證服務治理能力生效的同時,極小的影響微服務原有的服務性能,
雖然基于Java Agent的服務治理和基于SDK的服務治理在其原理上有所不同,但也避免不了微服務治理程序中產生對微服務原有性能的影響,基于Java Agent服務治理方式的相較于SDK的服務治理方式免去了侵入式的代碼開發,是一種運行時技術,所以還需要考慮更多方面性能優化問題,例如在啟動時間,運行時增強性能開銷等,本文將以Sermant的SpringBoot 注冊插件的性能測驗及優化程序為例,分享在Java Agent場景如何進行更好的性能測驗優化及在Java Agent下需要著重注意的性能陷阱,
SpringBoot 注冊插件為SpringBoot應用提供服務注冊發現能力,可用于在不修改原有代碼的前提下快速從ESB架構演進為微服務架構,在該插件中包含針對域名的替換能力,服務注冊發現能力,請求的超時重試等,為架構的成功演進,原有架構中基于域名的請求呼叫,將會被基于注冊資訊的請求呼叫(通過該插件的服務注冊發現能力,獲取服務提供者注冊的資訊)所取代,如下圖所示:
在域名處理的程序是必然會參與到呼叫程序中的,這是服務治理能力對業務性能影響的典型場景,
二、測驗方案
眾所周知,Java Agent程式和被增強應用運行時同行程,Java Agent程式最重要的是不能對被掛載的應用產生例外影響,導致應用不可用,所以Sermant在運行時的處理性能及穩定性等做多方面的測驗考量,在針對微服務進行測驗的程序中,我們往往只需要關注該微服務的性能即可,通過壓力測驗來檢驗微服務的服務提供能力,由于服務治理能力并不直接提供服務,我們更多地需要關注在開啟服務治理能力時,對微服務本身服務提供能力的影響,所以我們在測驗方案中需要進行對比測驗來評估服務治理能力的好壞,
本對照測驗中,我們通過壓力測驗讓系統達到極限場景(consumer端的CPU已經到達瓶頸),來分析攜帶Sermant并啟用服務治理能力時,對應用原有服務提供能力的影響,此處采用兩種部署方案
- 不攜帶Sermant,基于網關的場景,是架構改造前的運行模式
- 攜帶Sermant的場景,是遷移后的微服務架構運行模式
注:在這種對比測驗中,基于Java Agent的服務治理只需要對攜帶Java Agent程式和不攜帶Java Agent程式的場景進行對照測驗即可,無需兩套代碼進行對照測驗,
由于Java Agent程式和被增強應用處于統一行程,資源共享,基于上述兩種部署方案進行測驗,以不攜帶Java Agent程式作為測驗分析的對照組,就可以很清晰的看出引入Java Agent程式后產生的影響,并可根據對照結果進行優化,應用于Sermant上,就可以很容易的分析出Sermant的服務治理能力對微服務本身服務提供能力帶來的影響,
三、性能分析
由于需要針對應用發起的請求通過位元組碼增量的方式做域名的替換,SpringBoot 注冊插件通過對HttpClient、Openfeign、Okhttp等http客戶端進行了位元組碼增強,我們根據上一章節中的測驗方案對該插件提供的服務治理能力進行了測驗,下面我們以HttpClien為例通過CPU火焰圖來講述如何在Java Agent場景下分析性能瓶頸:
在性能調優程序中,我們可通過CPU火焰圖來分析性能瓶頸,火焰圖可以稱之為性能問題分析的"X光",可以很一針見血的看出在程式運行中哪些代碼片段產生了例外的CPU占用,可以參考《使用火焰圖(FlameGraph)分析程式性能》進行學習,當然,采集CPU火焰圖的方式很多,我們只需要學會如何看懂火焰圖即可,
分析步驟
1. 找到位元組碼增強邏輯的CPU占用
在分析程序中,首先需要找到位元組碼增強時選中的被增強方法(本文場景增強方法為InternalHttpClient::doExecute),位元組碼增強需要被增強程式的原有方法呼叫觸發,所以也可以很清晰的在CPU火焰圖中可以看到,Sermant實作的邏輯呼叫堆疊在被增強方法之上,在位元組碼增強邏輯執行結束后,被增強方法還會繼續執行,
所以除被增強方法執行的呼叫堆疊及CPU時間片占用外,皆為位元組碼增強所引入邏輯,在性能優化中需著重關注,
2. 分析例外占用
根據CPU火焰圖原理,找出位元組碼增強部分,找出例外占用CPU時間片的呼叫堆疊,并進行程式的優化,如下圖所示紅框選擇部分,皆為位元組碼增強中引入的邏輯,占用了非常多的CPU時間片,由于位元組碼增強程式和被增強程式,這種例外的占用,將會嚴重影響原程式的性能,在針對Java Agent場景的優化中可著重優化
通過上述步驟,我們可以一目了然的看到我們通過Java Agent程式引入的CPU額外占用,具體占用原因本文就不一一分析,
四、性能陷阱
基于上述兩個章節的測驗和分析方法,在本文的最后,列舉出在Java Agent開發程序中經常會遇到的性能陷阱,這里也給出解決方式,可以在開發中注意:
減少反射使用
在位元組碼增強開發程序中,很多情況下,如果類加載器不同,針對被增強應用的類和方法往往需要通過反射去獲取并使用,在我們的性能分析中,反射是一個CPU占用的巨大陷阱,在有些被BootstrapClassLoader加載的類增強時,甚至反射占用了一個方法呼叫30%以上的CPU事件片,
下圖選中方法中,反射占用該方法呼叫中的大部分CPU時間片:
但是由于類加載器的限制,有些反射是必須要使用的,我們也可以通過一定的手段進行優化,比如快取通過反射獲取的類和方法,在位元組碼增強中,多次觸發增強邏輯時減少反射占用CPU時間片非常有效,
Method method = METHOD_CACHE.get(methodKey); if (method != null) { return Optional.of(method); } method = clazz.getDeclaredMethod(methodName, paramsType); METHOD_CACHE.put(methodKey, method);
通過上述步驟優化后,通過火焰圖來看,效果是非常顯著的:
注意位元組碼增強插樁選擇
在做位元組碼增強時的增強點選擇很重要,位元組碼增強添加Transformer后運行時分為兩種情況:
- transform:針對尚未被類加載器加載的類,如果添加Transformer,在類被加載時就會觸發位元組碼的轉換,
- retransform:針對已經被類加載器加載的類,如果添加了Transformer,則需要被重新加載后再進行位元組碼的轉換,
Java中被BootstrapClassLoader加載的類,如果想要進行位元組碼增強,就需要使用第二種位元組碼轉換的方式,可想而知,如果重新加載類再進行轉換必然沒有在類第一次加載時就進行轉換的效率高,
除上述原因之外,在增強啟動類加載器加載的類時,由于雙親委派機制的限制(只能向上委托,不能向下委托),往往都是需要大量使用反射(用于呼叫其他類加載器加載的類)來實作增強邏輯,
上文中也講到,不加節制的使用反射將會通過Java Agent程式嚴重影響被增強應用的性能,所以在開發Java Agent時,需要謹慎選擇增強的類,非必要不增強被啟動類加載器加載的類,
上述兩點是在Java Agent開發程序中最容易發生的向被增強應用引入的性能陷阱,除此之外,Java Agent也是由Java所開發,在開發程序中也需要注意不要引入常見的性能陷阱,
結束語
Sermant作為專注于服務治理領域的位元組碼增強框架,致力于提供高性能、可擴展、易接入的服務治理體驗,并會在每個版本中做好性能、功能、體驗的看護,廣泛歡迎大家的加入,
Sermant官網:https://sermant.io
GitHub倉庫地址:https://github.com/huaweicloud/Sermant
點擊關注,第一時間了解華為云新鮮技術~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/545690.html
標籤:Java
