王子鵬,張樹東,任仲山,胡建亞
(1.首都師范大學信息工程學院,北京 100048;2.北京市成像技術高精尖創新中心,北京 100000;3.中國科學院軟件研究所,北京 100223)
插樁是一種獲取軟件狀態的方法,是軟件性能管理工具的核心部分,常常使用于對大規模分布式系統的監控和跟蹤[1],一般實現方法是程序員對被監控軟件系統進行代碼指令注入,這些被注入的代碼可以實現各種自定義功能,例如:記錄功能函數的執行時間、調用序列、植入回調函數、收集所需的信息并將數據記錄到數據庫中等。現如今大多數的插樁工具使用的是動態二進制插樁技術,例如DTrace[2]、Pin[3]等工具,但這些工具通常只提供插樁本身的實現,并不會提供選擇插樁點的和插樁粒度的工作,這就導致當一些性能管理工具使用插樁技術時難以選擇適宜的插樁點和控制插樁粒度。傳統的性能管理工具選擇全插樁的策略,即將插樁點植入到被監控軟件系統 (下文稱作目標程序)所有運行的類中,這就導致了大量的插樁點產生了極大系統資源消耗,并且插樁點在目標程序運行的過程中是完全不會改變的[4-5]。在對目標程序的實際監控中,并不是所有的類都需要插樁,如果對一個目標程序的插樁粒度過粗,將可能導致達不到用戶對目標程序性能管理的要求,例如難以定位軟件發生異常的具體位置。如果對一個目標程序的插樁粒度過細,將產生大量的資源消耗,甚至影響到目標程序本身的運行。所以一個根據實際需要,動態自適應地改變插樁粒度的插樁框架不僅能夠滿足用戶的需求,而且能夠把資源消耗減少到最小,是十分必要的[6-7]。針對此問題,本文提出了一種全新的自適應插樁框架。
本文針對Java應用程序,提供一個基于機器學習的自適應插樁架構[8],可以針對軟件性能管理工具進行適當的插樁工作;本文可以識別不同來源的Java類,將插樁點植入到用戶關心的類中。本文還提出插樁粒度矯正機制[9],在被測程序運行時對插樁粒度進行動態控制,控制整個系統插樁資源的消耗。
本文的插樁架構是為性能管理軟件服務的組件,它可以在極小的資源消耗的情況的下進行插樁,得到跟蹤數據。本文的主要目標有以下兩點:1)對不同來源的Java類進行識別,將插樁點植入到用戶編寫類中。2)在被測程序運行中動態的對插樁粒度進行矯正。接下來,通過一個普通的例子,對問題進行建模。有一個簡單的Java編寫的Web客戶端Client,當此客戶端運行的時候,性能管理工具就需要獲取此程序的跟蹤信息,此時就需要插樁工具對此程序進行插樁。為了方便表述,這里將相關的概念和術語進行引入和說明。
C={c1,c2,…,cn}表示插樁類,即在被測程序中,要將跟蹤代碼注入到哪些類當中。
M={m1,m2,…,mn}表示插樁方法,即是在c中,將跟蹤代碼注入到哪些方法中。
p表示插樁點,即是在m中,將跟蹤代碼注入的具體位置。每兩個p,對應一個集合Sn={ps,pe},S是插樁點的作用域,表示這個插樁點的作用范圍,ps為插樁起始位置,pe為插樁結束位置。一個Sn也可以看作一個插樁探針,S={s1,s2,…,sn}。
I={i1,i2,…,in}表示插樁的內容,即是在插樁點上,被注入的具體代碼。
C中每個cn包括一個集合m,每個mn包括一個集合s,每個sn包括一個集合i。整個插樁信息的關系呈現出一個樹狀的關系,如圖1所示。
當被測程序運行時,插樁要進行如下步驟:
1)確定被測程序中所有的插樁類集合C;
2)篩選并確定cn插樁方法的集合M;
3)篩選并確定mn中所有的插樁點作用域集合S;
4)確定插樁點sn的中的ps和pe;
5)確定每個插樁點要注入的代碼集合I;
6)進行插樁。
當程序運行時,有:
其中類c1為用戶自定義的類,c2,c3,c4,c5,c6屬于Java提供的類庫中的類。在傳統的插樁方法中,不會區分用戶自定義的類和非用戶自定義類,選擇全插樁的策略,即對所有類進行分析和插樁。但是,全插樁的策略會造成極大的資源消耗和浪費,而且用戶在目標程序實際運行中,不關心其他類運行的情況。所以為了解決此問題,本文提出了一種基于樸素貝葉斯算法的分類模型,可以區分不同來源的Java類,將來自用戶編寫的類,JDK中的類,第三方開發的工具類區分開。除此而外,本文能夠在日常對目標程序的插樁過程中,依照目標程序的性能變化,自動地改變插樁的粒度,減少插樁的資源消耗。
圖1 插樁的樹形關系圖
在本章里,本文將主要介紹本文的體系結構以及進行插樁的過程。
圖2顯示了本文的高層體系結構以及插樁過程。如圖所示,自適應插樁體系中,主要有4個部分,分別是目標程序,Java虛擬機,Agent以及客戶端。
目標程序:指的是要被插樁的Java程序。
Java虛擬機:Java程序運行在Java虛擬機上,Agent在插樁的過程中不直接與目標程序進行交互,而通過Java虛擬機完成對目標程序信息的獲取以及對目標程序進行插樁。這樣可以使得插樁對目標程序本身造成較小的干擾,保證了目標程序本身的獨立性和安全性。在圖中,JVMToolInterface是Java虛擬機提供的一個工具接口,它是在JavaSE5提出的一個工具。JVMToolInterface提供接口可以獲取Java虛擬機的狀態以及運行在Java虛擬機上的程序的狀態信息,也可以控制這些程序的執行,具有廣泛的用途。本文則通過JVMToolInterface來獲取目標程序的信息。JavaInstrumentation是JavaSE6的一個新特性,它最大的特點在于可以在目標程序運行時動態的將目標程序的類進行修改,本文則利用它的這一特點,對目標程序進行動態二進制插樁。
Agent:Agent是整個插樁體系結構中最核心的部分,絕大部分的功能都在 Agent中實現,主要包括了以下幾個部分:
裝載器:裝載器主要與Java虛擬機進行交互,它的主要功能是提取運行在Java虛擬機中的程序的信息,主要是提取目標程序要運行所需的全部類,并將這些信息寫入目標程序類表。
分類器:分類器的作用就是將不同來源的三種Java類區分開,對用戶關心的類進行插樁,這種方式可以極大地減少資源的消耗。分類器初步的確定了插樁類集合C,分類器的具體實現將在第三章進行詳細說明。分類器將判定為用戶編寫的類的寫入用戶類表中。
分析器:分析器的主要功能對分類器初步確定的插樁類集合C進行分析,得出每個類的插樁方法集合M,作用域集合S,插樁點集合P,插樁內容集合I。
插樁表:插樁表是本文Agent的核心數據表,它保存了在目標程序中所有的插樁點。除了分析器對它進行寫入外,日常由自適應器對它進行操作。執行器會依照插樁表中的信息去實施插樁。
執行器:執行器的主要功能是依照插樁表中的內容進行實際的插樁工作。執行器是利用Javassist類庫完成的[10]。它提供了對Java程序字節碼進行操作和修改的方法,可以在Java程序運行的過程中對程序類進行修改,優點在于簡單和快速。本文執行器使用Javassist類庫,通過JavaInstrumentation接口對目標程序字節碼進行修改,達到探針注入的目的。
數據回收器:數據回收器的主要工作就是回收目標程序的性能數據信息,數據回收器會依照一定的采樣策略去回收數據。
自適應器:自適應器是本文Agent的核心部件,它的主要工作是動態地調整插樁粒度,達到減少資源負載目的。自適應器的工作時期是在插樁程序日常運行的階段。自適應器是根據目標程序的性能數據反饋,利用基于機器學習的算法處理和分析性能數據信息,找出異常的程序執行路徑,對其進行更細粒度的插樁,定位異常根源,具體的實現將在第四章中詳細說明。
客戶端:客戶端的主要功能是向用戶展示目標程序的性能數據。
本文將Agent對目標程序的插樁過程分為兩個主要階段:1)初始化階段,2)自適應階段。如圖2所示,初始化階段用點虛線表示,自適應階段用實線表示,初始化與自適應都要進行的共同階段用短劃線表示。其中,初始化階段的執行步驟用圓括號表示,例如:[2],自適應階段的執行步驟用尖括號表示,例如:<1>。
1)初始化階段。
圖2 自適應插樁的高層體系結構以及插樁過程
初始化階段主要指的是當本文Agent第一次插樁一個目標程序的某個類所要經歷的階段,本文將這個過程稱為初始化過程。當一個Java程序運行時,如果它的源代碼是Java格式的文件,它的源代碼將會編譯成為.class格式的文件并加載在Java虛擬機上運行,如果是.class格式的文件,將直接加載在Java虛擬機上運行 (步驟 [1]),這是所有Java程序運行的必經階段。當目標程序運行之后,本文Agent進入初始化階段,本文 Agent的裝載器通過 JVMToolInterface將目標程序的所有類信息獲取 (步驟 [2]),并存入目標程序類表 (步驟 [3])。當Agent獲取到目標程序類之后,分類器開始工作,從目標程序類表中讀出這些數據并進行分析 (步驟 [4])),分類器將利用基于機器學習的算法將這些類分類,并找出其中屬于用戶自定義編寫的類,并把這些類的信息寫入用戶類表中 (步驟[5])。然后分析器讀出用戶類表中的用戶類信息 (步驟[6])。此后,分析器對這些類進行分析,得出具體的插樁信息,這些信息包括插樁類集合C,插樁方法集合M,插樁探針集合S,插樁點集合P,以及插樁內容集合I。并將這些信息數據寫入插樁表中 (步驟 [7])。當得到插樁信息后 (步驟[8]),執行器開始工作,將通過JavaInstrumentationInterface把信息交給Java虛擬機 (步驟 [9]),此時將重定義目標程序的類,如圖2所示為步驟[10],探針被注入進目標程序。當目標程序進入到特定的階段后,探針將返回數據到數據回收器 (步驟[11]),數據回收器通過一定的插樁策略進行采樣,將目標程序的性能數據寫入到回收數據庫中 (步驟 [12])。此時則插樁的初始化階段完成。
2)自適應階段。
在完成初次插樁之后,為了更快、更準確、更高效地發現應用中潛在的問題,需要根據監測數據對插樁的粒度進行動態調整,這是因為當對一個目標程序的插樁粒度過粗,將導致無法準確定位發生異常的具體位置;如果對一個目標程序的插樁粒度過細,會導致應用性能嚴重下降,同時會帶來大量的資源消耗。所以本文在目標程序正常啟動運行后會進入自適應調整階段,對插樁粒度進行動態的調整,達到滿足性能管理需求的同時將插樁資源消耗減少的最小。
當Agent完成初始化階段之后,當執行初始化階段的插樁函數時,將能夠收集到目標程序的性能跟蹤數據。此時,自適應器將請求讀取并得到這些數據,如圖2所示為步驟<1>,步驟<2>。自適應器將會對這些性能跟蹤數據進行分析,通過基于機器學習的算法,會得出一個正常性能達標范圍,當一段目標程序的跟蹤路徑的性能數據不在這個范圍之內時,就說明這個跟蹤路徑可能出現了性能問題,此時自適應器會將此跟蹤路徑的插樁粒度變細,增加更多的插樁點。相反,對于一段執行路徑的性能在設定時間內性能滿足要求或當某些有問題的跟蹤路徑性能恢復正常,自適應器會通過去除一些插樁點的方法將該路徑的插樁粒度變粗。自適應器將通過修改插樁表完成這些變更,如圖2所示為步驟<4>,并通知執行器插樁信息發生了改變,如圖2所示為步驟<5>,此后,執行器會執行調整后的插樁表,和初始化階段中的步驟相同,為步驟<6>,步驟<7>,步驟<8>,步驟<9>,步驟<10>。除此而外,用戶也可以通過客戶端手動修改插樁粒度。
本文通過在應用執行中自適應調整插樁的粒度,實現了在最小的插樁資源消耗下,滿足用戶對目標程序的性能管理的需求。
本文的插樁架構是針對Java程序,在本章內將詳細介紹本文核心功能部分的實現,依次將介紹1)基于樸素貝葉斯算法的分類器的實現[11]。2)插片式分析器的實現。3)基于機器學習組合算法的性能自適應器的實現[12-13]。
對于Java程序來說,其代碼一般來自3個部分。開發人員自定義編寫的代碼,調用JDK類中的代碼以及借助第三方提供的庫。它們有各自的類,在Java程序運行時共同作用,形成完整的Java程序。但是,對于性能管理工具來說,在程序的實際運行中,用戶通常只關心他們自己編寫的類的性能表現,而傳統的性能管理插樁策略是對所有的類進行插樁,這樣就造成了大量的資源消耗。所以,在插樁前,將用戶自定義編寫的類,系統類庫中的類以及第三方類庫中的類區分開,只插樁用戶自定義的類,這樣可以很大的減少插樁資源負載,增大插樁工具的實用性,是十分有必要的。
3.1.1 采集數據
對于本文的類分類器來說,數據就是一個個類的信息,為了使數據能夠有足夠的代表性和得到廣泛的認可,本文完成了對常用的Java開源類庫中類信息數據的采集,分別對自定義類、系統類庫、第三方插件庫中的類進行了采集;包括第三方插件庫 Maven、Solr、Commons Math、Common-Configuration等在內,一共收集到77 000多個類的信息。
3.1.2 特征選擇
本文的目的是為了區分不同來源的Java類,為此將盡可能選取有明顯區分意義的特征。
1)對于一個Java類來說,程序通過類名對其進行調用,類名也是對不同的類進行區分的最直接的標志,所以選取類名為第一個特征。
2)在Java程序的運行機制中,源代碼被編譯為.class格式的文件進行加載使用,每一個.class文件通常代表一個類,當程序需要使用某個類時,Java由類加載器來加載此類。在Java的運行機制中,有雙親委派模型,由三種類加載器來加載不同類型的類,分別是啟動類加載器,擴展類加載器,應用程序類加載器。啟動類加載器加載路徑<JAVA_HOME>/lib中的類,擴展類加載器加載路徑<JAVA_HOME>/lib/ext中的類,應用程序類加載器加載其他類,由此可分辨出一個類是否為JDK中的類,其加載器的類型選取為第二個特征。
3)保存Java類信息的文件會存放在某個路徑下,一般情況下,系統類存放在<JAVA_HOME>下,第三方類庫的類文件單獨存放或者和用戶自定義類存放在程序的目錄下;由于不同類型的類文件一般存放在不同的路徑下,因此本文將Java類存放的文件的路徑選取為第三個特征。
4)在程序的目錄下,大多數情況下,不管是自定義類文件還是第三方插件的類文件都會保存在Jar格式的壓縮包中,但仍然有少數類文件不在Jar包中,此類型一般屬于用戶自定義的類,選取為第四個特征。
5)一般來說,對于一個存放類文件的Jar包,如果名字可以和程序的名稱匹配或相似,這個Jar包中的類文件則是由用戶自定義開發的。類文件所在Jar包的名稱可以選取為第五個特征。
6)對于程序來說,生成項目會改變此程序的文件,從而改變了這些項目文件的最后修改時間,查看一個類文件的最后修改時間是否和程序一致,也可作為判斷類文件是否屬于程序的特征。
3.1.3 樸素貝葉斯分類模型
樸素貝葉斯分類模型是基于貝葉斯定理的分類模型,每個特征都相互獨立。樸素貝葉斯分類器的優點在于算法簡單,需要的訓練數據量較少,且在實際的分類中處理中資源負載很小,它的分類過程如下:
1)對于一個數據樣本X={x1,x2,…,xn},其中x1,x2,……,xn表示數據樣本 X的 n個特征。C={c1,c2,…,cn}表示n個類的集合,數據樣本X可能屬于的C中的某個類。對于未標記的數據樣本X,其屬于類ck的概率為P(Ck|x1,x2…,xn)。
2)根據貝葉斯定理:
3)根據數據集可以訓練得出:
4)對于未知樣本數據X,其分類為y,y∈C
3.1.4 分類器的實驗和評估
將采集的數據67 000余條用于學習,10 000條用于評估。實驗結果如表1所示。
表1 貝葉斯分類器學習和預測結果
貝葉斯分類器對Java類進行區分時,正確預測用戶類4 172個,系統類2 727個,第三方類,1 487個,總計個數8 406,準確率可達到84%,對系統類的預測準確率較高,準確預測所有的系統類;可以將所有的第三方類標記成功,但是對用戶類的預測率較低,5 766個用戶類只預測出4 172個,真實預測準確率為72.35%,將一些用戶類預測為第三方類。其原因主要為一些用戶類的Jar包的名字并不與項目名稱匹配或相似,且沒有存放在目標程序的路徑下,最后修改時間與主項目不一致。遇到此種情況時,在實際中,可以手動將一些Jar包信息加入用戶類中。
Pinpoint的是一個開源的軟件性能管理軟件[5],在它的插樁部分,對軟件不同的部分采用不同類型的插樁插件進行對應分析,一些常用組件都有與之對應的插件進行插樁分析,例如:http,mysql-jdbc,Spring等插件。本文借鑒了Pinpoint的插樁思路,采用插件式的分析器組件。目標程序不同的部分都有與之對應的分析插件進行分析,所有插件分析的結果之和構成目標程序初始化階段插樁分析的結果。一般情況下,在初始化分析階段,本文傾向于使用盡可能粗的插樁粒度。
采用基于插件組合的插樁分析的策略,既能夠保證插樁的專業性,也能夠通過增加插件來實現擴展性,從而使得本文的方法具有很強的靈活性。
3.3.1 插樁粒度
一個軟件可以實現不同的功能,例如通過網絡傳遞消息或者和數據庫交互,軟件實現不同功能的代碼執行路徑不同,插樁粒度的情況也不相同。實現不同功能都應該有不同的插樁粒度等級,本文采用插件式的插樁粒度等級,即不同插件的插樁粒度等級都有專門的劃分。
3.3.2 消息傳遞模型的插樁粒度
接下來將通過一個用戶請求瀏覽網頁的例子,將說明http消息傳遞的插樁粒度等級。此粒度等級不僅適用于http消息傳遞程序的插樁,也適用于分布式環境下大部分程序的其他類型消息傳遞的插樁。
如圖3所示,一個用戶客戶端請求訪問一個網頁,此網頁由兩部分的內容組成,文字和圖片,分別來自不同的服務器。當一個客戶端發送一個瀏覽請求Request1時,首先到達網頁服務器,此時網頁服務器會發現需要文字和圖片的數據,于是發送請求Request2和Request3到文字服務器和圖片服務器,文字服務器和圖片服務器分別立即做出反應,將消息返還給網頁服務器,分別是Reply2和Reply3。然后網頁服務器響應最初的請求,將請求返還到客戶端Reply1。
圖3 簡單的http消息傳遞
以上是一個簡單分布式消息傳遞過程,對于分布式程序的跟蹤實現,Google提出的分布式系統跟蹤框架Dapper給予后來的開發者很大的啟發[1]。在具體的分布式消息跟蹤實現上[14-15],本文也采用Dapper的分布式跟蹤理念,將一個完整消息請求和返還鏈路稱為一個trace。
本文在對分布式系統消息傳遞插樁的最粗粒度為trace等級,即記錄完成一個完整trace所需的時間和其他信息。
第二等級的插樁粒度是server等級,即記錄一個服務器收到消息到發出消息的時間和其他信息。例如:圖3中文字服務器接收到Request2時間到發出響應Reply2的時間。
第三等級的插樁粒度稱為method等級,即記錄一個方法執行所需的時間和其他信息。
以上三種等級的插樁粒度在目標程序的執行過程中隨著程序的執行情況改變,在達到用戶要求的情況下,確保最小的插樁資源負載。
3.3.3 特征選擇
本文的性能自適應插樁器是利用目標程序的性能數據作為反饋,使用基于機器學習的組合算法進行處理,找出異常的性能數據,改變其執行路徑的插樁粒度。本文希望可以在最小的插樁資源消耗下找出異常情況,即盡可能少的提取特征去判定。
特征提取:在分布式系統的消息傳遞中,消息的響應時間是衡量目標程序的某個執行路徑是否出現異常的最主要因素。所以選取數據傳輸響應時間為主要特征。
假設一個trace的總響應時間為T,這個trace的消息要經過n個節點的處理,那么有:
其中:ti表示第i個節點傳輸數據的響應時間,pi表示第i個節點上程序處理請求所需要的時間。假設P=對于不同的trace來說P是不相同的,因此無法橫向評估不同trace的時間數據。但是,對同一個trace來說,P大致上相同的,本文則通過同一個trace的歷史數據去評估此trace是否異常,此時有:
P對于同一個trace的歷史數據來說可以看作為一個常量。
影響數據傳輸響應時間的因素有很多,例如服務器硬件性能,網絡帶寬等等。但是在同樣的環境下,影響數據傳輸響應時間的因素主要為數據包的個數和大小,數據包越多,數據量越大傳輸所需要的時間越長。
在實際的傳輸過程中,傳輸時間受數據包大小和數據包個數的共同影響,數據包越少,傳輸時間越短。但是,并不能將所有數據都分在一個數據包內,因為最大數據包的大小受到最大傳輸單元的限制(MaximumTransmissionUnit),以下簡稱為MTU,MTU的單位為字節,當所傳輸的數據量大于一個MTU時,此時發送的數據就會被分為多個包,每個包的大小都不大于MTU。
如圖4所示,圖中為trace總響應時間和數據包大小的關系,其中X軸代表數據包含有MTU的個數,Y軸代表trace響應的時間。MTU_s是發送一個足夠小的數據包需要的時間,MTU_e代表發送一個大小為MTU的數據包所需要的時間。當x的區間為 (k,k+1)時,響應時間Y與x成正比例遞增關系,每當數據量超出一個MTU時,響應時間就會因為多出一個數據包而增加MTU_s,趨勢呈現階梯形增長。
圖4 trace總響應時間和包大小的關系
當一個trace中傳輸的數據量大小超過MTU時就會被分成k個盡可能大的子包,每個子包的大小不超過MTU,其總響應時間在區間之間。
影響trace的響應時間的因素分別是數據量的大小,包的個數與MTU的大小。選擇數據量的大小為第二個特征。MTU的大小在一般的生成環境中是固定不變的,對其視為常量,不選為特征。
在實際的生產環境中,傳輸數據時會設有數據緩沖區,當緩存的數據足夠接近一個MTU大小時才會發送。假設傳輸數據量為Size,數據包的個數為Num,則Num有以下式子表示:
雖然此時得到的Num會與實際Num有一定誤差,但誤差尚在可容忍范圍內。若選取數據包數量為特征,則需要大量的插樁點獲取數據,會造成很大的資源負載。綜合考慮到實際插樁資源的消耗,盡可能的減少插樁點,所以不選數據包數量為特征。
3.3.4 性能數據線性回歸模型
由上一節可知,響應時間和數據傳輸量是分布式系統消息傳遞的特征,采集到的trace的性能數據將是一個響應時間和數據傳輸量的數據集合,記為:
其中:(xi,yi)表示當數據量大小為xi個MTU時,其響應時間為yi。當x發生改變時,y隨著x的改變而改變,當x在區間為 (k,k+1)內時,其中的正常數據點可以通過一元線性回歸模型擬合成一條直線段,稱為性能擬合回歸線,如圖5所示。
圖5 性能擬合回歸線
線性回歸模型的模型函數為:
對以上參數利用最小二乘法進行估計:
其中:
有r表示樣本x與y之間的相關系數,用來衡量擬合的回歸的好壞。
回歸方程的誤差為ε,ε~ (0,σ2),利用離差平方和計算誤差:
3.3.5 噪聲處理
如圖5所示,trace的性能數據不僅包括正常的數據點,而且包括出現性能異常的數據點。出現性能異常的情況一般包括兩種:延遲和超時。延遲指出現性能問題,雖然能夠成功傳輸數據,但是因為硬件環境、網絡等緣故出現明顯的響應時間過大情況。超時指不能夠成功傳輸數據的情況,一般的超時時間遠遠大于發送單個數據包的時間。在圖5中,除了正常的數據點外 (在圖中用圓形表示),還包括2個延時數據點 (在圖中用矩形表示),2個超時數據點,(在圖中用叉點表示)。超時時間Out_time>>MTU_e>MTU_s。
當用性能數據擬合成性能回歸線時,由于異常數據點的存在,會對擬合成的線造成極大的影響,無法反應正常的情況,所以就需要將這些異常的點或者疑似異常的點去掉,由正常數據點的集合擬合成性能回歸線。
K-Means算法是一種經典的基于距離的聚類算法,采用距離作為相似性評價指標,可以將N個數據對象劃分為K個類,同一類對象之中,對象之間的相似度較高,不同類對象之間的相似度較小。本文在K-Means算法的基礎上對其進行改進,改變相似度的計算方式,工作流程如下:
1)從N個數據對象中隨機選擇K個質心。
3)重新計算每個類的質心
4)重復第2步和第3步,直到質心不在變化或者變化小于預先設定的閾值。
除此而外,使用改進的K-Means算法,簡單、快速,造成的資源負載較小。
3.3.6 數據預處理
為了方便對性能數據進行判別,就需要對原始數據進行預處理。原始的性能數據將是一個響應時間和數據傳輸量的數據集合O,O={(size1,time1),(size2,time2),…,(sizei,timei)}。
size表示傳輸數據量的大小,time表示響應時間。根據上文可知,僅當數據量大小范圍在 (k,k+1)MTU時,數據點可擬合為一條線段,則記x為含有MTU的個數。根據上文可知:
x在每個范圍 (k,k+1)都能擬合成線段,而且擬合的線段斜率相等,因為其擬合線段的相似性,可以歸納得一般的模型,利于處理,此時將要求x的范圍 (0,1),相應地對time項進行預處理得到:
最后,預處理后的性能數據集合為:
3.3.7 性能數據處理過程
算法輸入:原始性能數據集
步驟1:生成預處理性能數據集
步驟2:使用K-Means算法對數據進行分類,SetK=3,將原始數據分為三類,一類為正常數據,一類為延遲據,一類為超時數據
Get P1={(x1,y1),(x2,y2),…,(xi,yi)},x∈(0,1)為正常數據類
Get P2={(x1,y1),(x2,y2),…,(xi,yi)},x∈(0,1)為延遲數據類
Get P3={(x1,y1),(x2,y2),…,(xi,yi)},x∈(0,1)為延遲數據類
步驟3:處理P1數據集,將P1數據集中y大于MTU_e的數據點去除,得到數據集合P4。
步驟4:使用P4進行一元線性回歸擬合,得到性能擬合回歸線:
步驟5:使用性能擬合回歸線對P1、P2進行判斷
設置異常數據集R
foreach(xi)do
{if);
elseR.add();}
Get為異常數據集合
算法輸出:異常數據集合
3.3.8 自適應器異常判斷的實驗和評估
本實驗數據來源主要是對遠程網路地址發送報文,并記錄報文大小和響應時間。
經過多次實驗,本文實驗環境中,MTU大小為1 500字節,MTU_s約為2 ms,MTU_e約為4 ms。向遠程服務器發送不同大小的數據包,共發送6 000條報文,得到報文大小和響應時間,用基于組合機器學習算法的自適應器進行處理,得到表2。
基于組合機器學習算法自適應器對性能數據進行預測,正確預測正常點4 656個,延遲點327個,超時點344個,總計個數為5 327,正確率為88.78%。其對超時數據點和延遲數據點的都能夠完全準確判別。但是對數據的延遲十分敏感,將一些正常的數據點判別為延遲數據點,這是由于在數據處理的過程中,為了處理噪音,盡可能的保留擬合回歸線的數據點,得到的誤差偏小,將一些波動較大的正常數據點判別為延遲數據點,這樣會導致插樁點的數量變多。在實際的插樁過程中,可以根據實際的延遲容忍情況,可以在數據點判別時,適當增大誤差值,減少插樁點。
表2 性能數據處理結果
在本章,本文將使用自適應插樁架構對的目標程序進行插樁,并與Pinpoint的插樁策略在同等的條件下的插樁情況進行對比,評估本文插樁架構對目標程序產生的影響和對系統產生的影響。
為了驗證本文插樁架構,實驗的目標程序是基于Java開源電子商務網站架構Broadleaf的購物網站HeatClinic[16]。HeatClinic是一個多層架構的網站,主要使用SpringBoot框架開發。實驗所使用的數據庫為MySQL,網站的部署在Tomcat應用服務器上。
本文對HeatClinic網站的業務進行實驗,將通過壓力測試工具JMeter對插樁過后的HeatClinic網站進行測試,查看不同壓力規模下,對目標程序和系統產生的影響。主要查看網頁響應時間和吞吐量。網頁的響應時間來衡量監控工具對目標程序的干擾情況,吞吐量來衡量插樁資源負載情況。壓力測試中,線程在100 ms內完成啟動。
本文實現兩種不同情況的實驗:1)正常情況下的實驗,即網站沒有發生異常。2)異常情況下的實驗,即網站出現異常產生響應延時,此種情況可以由向網站注入線程延遲代碼模擬。本文將在不同的方法中注入延遲時長不等的延遲點。
4.3.1 插樁對應用性能造成干擾的分析
圖6顯示了在相同并發用戶數的情況下,不同插樁策略的網頁平均響應時間隨著發生異常的變化情況。在目標程序運行初時,Pinpoint和本文分別對目標程序進行插樁,使目標程序正常運行一段時間,得到目標程序正常運行的性能數據范圍。本文插樁架構完成初始化階段并進入自適應階段。在程序運行到一段時間后,圖中為a時間點,動態的對相關方法注入延遲代碼,造成異常,產生響應延遲Δt。使用全插樁策略的Pinpoint和本文的插樁不會改變插樁粒度,目標程序網頁的平均響應時間增加Δt。本文的自適應插樁在目標程序發生異常后收集到性能數據,并分析得出程序發生異常,在a時后的一段時間,使程序的插樁粒度更細,增加插樁點。此時目標程序的網頁平均響應時間增量超過Δt,達到全插樁策略。在程序運行到b時,去除延遲代碼,本文自適應插樁收集到性能數據并分析得出程序運行正常,調整插樁粒度,減少插樁點,對目標程序的干擾變小,平均響應時間減少。本文的自適應策略與Pinpoint全插樁策略相比,在對目標程序監控時,平均的響應時間更少。
圖6 發生異常時的網頁響應時間的變化情況
4.3.2 插樁資源負載分析
圖7顯示了在相同并發用戶數的情況下,且計算資源充足,不同插樁策略的網頁平均吞吐量隨著發生異常的變化情況。在a時刻注入異常代碼,隨著響應時間增大,無插樁以及Pinpoint全插樁和本文全插樁的網頁吞吐量會受到影響并減少,對于本文自適應策略來說,發生異常初始,吞吐量減少,但隨著診斷出異常并增加插樁點后,需求系統資源變多,吞吐量增大。在b時刻移除異常后,各個策略的吞吐量均會恢復到正常水平。
圖7 發生異常時網頁吞吐量的變化情況
4.3.3 不同插樁策略對比分析
表3展示不同策略下目標程序網頁的平均響應時間和吞吐量。使用 Pinpoint和對目標程序進行插樁,10、50、100用戶并發平均響應時間比不使用插樁分別多了22.9%、15.38%、8.98%,使用本文的自適應插樁架構的情況為13.63%,7.17%,2.81%。使用本文的自適應插樁比使用Pinpoint全插樁平均的響應時間要減小6.88%,本文的插樁策略對目標程序造成的干擾更小。使用本文自適應插樁,在目標程序的運行過程中,平均網頁吞吐量更小,平均插樁點比Pinpoint全插樁少,需要的系統資源更少。
表3 不同插樁策略對比
在分布式系統中,對于分布式應用出現的異常進行診斷和定位是十分重要的。在對這些異常進行診斷的過程中,現今研究主要目標是將插樁的資源消耗和對目標程序的干擾降低到最小,可以使插樁技術在實際的大規模分布式系統的中得到應用。雖然一些插樁工具被一些大公司使用在其生產環境,如 Twitter、Google、京東、阿里巴巴等,但是,到目前為止大部分的研究工作將重點放在了改進插樁過程的本身[17],或者以不同的原理實現分布式跟蹤[18-19],如Facebook最新的工作[20],達到優化插樁的目的。但是,插樁資源的主要消耗和對系統的干擾來自于產生和收集大量的性能數據,在插樁運行的過程中,自適應調節插樁點可以有效的減小插樁資源消耗與干擾。
現今仍然有一部分研究工作針對自適應插樁,AIM[6]是針對于軟件性能分析的自適應插樁框架,它能夠在插樁過程中依靠指令增加和減少插樁點,但是指令卻需要通過其客戶端人工輸入。DOBI[17]兼顧了插樁的精準性、完整性和性能消耗,使用基于方法執行時間的分析的模型進行自適應插樁,但是插樁粒度過細,產生大量消耗。RaceTrack[21]是針對.net框架下程序異常診斷的自適應插樁框架,主要對線程進行分析,對異常線程加入更多的插樁點,問題在于在大規模的分布式系統中線程眾多且復雜,并且難以收集綜合分析。APMP[22]對每個插樁點都產生權重,通過權重分析決策增減插樁點,缺點在于仍然需要大量的信息支持,會造成不少資源消耗。SSSM[24]提出了一種利用性能數據進行響應時間預測的策略,為了判定異常,采用了多個維度、細粒度的響應時間回收策略,進而產生大量的插樁點。本文認為在復雜多變的分布式環境下,很難通過一種或多種預先設定的策略或標準去判斷發生性能延遲,使用機器學習算法,學習實際的數據,分析性能曲線,是一種客觀有效的方法。
本文提出了一個基于機器學習的自適應插樁框架,在對目標程序運行的過程中,不僅能夠動態地進行插樁,而且能夠依據目標程序的性能數據動態的調整插樁粒度,在診斷異常的同時,將插樁資源消耗減少到最小。未來的研究方向主要是以下幾個方面:1)將對現有性能數據處理的算法進行改進,主要提高對異常數據點識別的準確率和降低處理算法的資源消耗。2)完善自適應規則,對于不同類型的軟件所產生的性能數據是不一樣,所以就應該有相對應的、更專業的自適應規則去匹配,希望通過插件的形式可以完善自適應規則,保持本文的活力。3)采樣策略,雖然本文在極力減少插樁點,但是這些插樁點也會產生很多性能數據,有些性能數據價值較大,有些價值較小[21],如何在保持性能消耗不變甚至減小消耗的情況將有價值大數據點辨別出來并記錄下來,拋棄無價值的數據點也是我們未來的工作之一[25]。
本文提出了一種全新的自適應插樁架構,其核心是利用機器學習算法分析性能數據并根據得到反饋對Java程序進行動態插樁,本文的方法依據目標程序實際運行情況動態調整插樁粒度。本文解決了用戶在生產環境中應用執行狀態追蹤和性能監測中的兩個主要的問題:第一,用戶往往只關心他們自己編寫的類的運行情況,而對于其他來源的類,插樁會造成較大的資源負載,所以本文提出基于樸素貝葉斯算法的分類器,只將用戶自己編寫的類找出并分析它們,減小了插樁資源消耗。第二個問題是傳統的插樁工具往往不能令人滿意,插樁粒度粗則會導致無法定位異常,插樁粒度細則會導致資源負載過大。本文將基于機器學習的組合算法引入性能數據處理,分析性能數據并動態調整插樁粒度。