熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

Java運行時監控,第1部分: Java運行時性能和可用性監控

2013-11-23 18:44:48  來源: Java核心技術 

  摘要運行時性能監控對於實現和維護性能優異的系統至關重要本文是系列文章的第 部分(共 部分)Nicholas Whitehead 將闡述如何有效地對 Java? 性能執行低級粒度的監控您生成的數據可以很好的反映系統操作的內在狀態並揭示影響環境穩定性及性能的約束和因素

  簡介

  當今的許多 Java 應用程序都依賴於一組復雜的分布式依賴關系和移動部件很多外部因素都可能對應用程序的性能和可用性造成影響這些影響基本上都無法完全消除或解決且難以在預生成環境中准確模擬Stuff happens但是您可以創建並維護一個全面的系統來監控應用程序的整個生態系統從而顯著降低這些事件的嚴重性和持續時間

  本系列文章給出了實現此類系統的一些模式和技巧模式以及我將使用的一些術語都表示泛指通過結合示例代碼和插圖它們將幫助您理解應用程序性能監控的概念這種理解強調解決方案的必要性並能幫助您選擇商業或開源的解決方案您可以擴展和定制一個解決方案或者根據需要將其作為設計解決方案的藍圖

  第 部分


探究應用程序性能管理(APM)系統的屬性
描述系統監控的常見反面模式
列舉監控 JVM 性能的方法
提供有效插裝應用程序源代碼的方法

  第 部分將重點介紹插裝 Java 類及資源而無需修改原始源代碼的方法 部分將論述監控 JVM 外部資源的方法包括主機及其操作系統以及數據庫和消息傳遞系統等遠程服務它還將總結並歸納其他的 APM 問題如數據管理數據虛擬化報告和報警

  APM 系統模式和反面模式

  為讓大家正確入門應當強調雖然此處介紹的多數與 Java 相關的內容看上去與應用程序和代碼性能分析的流程類似但其實並非 如此性能分析是一個極具價值的生產前流程它可以確認您的 Java 代碼是否可擴展高效快速和足夠出色但是根據 stuff happens 公理當您在生產中遇到無法說明的問題時優秀的開發階段代碼性能分析可能無用武之地

  我的意思是在生產中實現性能分析的一些方面並從運行中的應用程序收集一些相同的實時數據及其所有外部依賴關系該數據由一系列遍及目標的定量測量指標組成它們為整個系統的健康狀況提供細粒度和詳細的表示此外通過保留這些指標的歷史庫您可以捕獲准確的基線以幫助您確認環境仍然健康或查明特定缺陷的根源和規模

  監控反面模式

  完全沒有監控資源的應用程序微乎其微但仍然需要考慮這些反面模式它們經常出現在運行環境中


盲點某些系統依賴關系未受監控或者監控數據不可訪問運行中的數據庫可以覆蓋所有監控范圍但如果受支持的網絡無法全面覆蓋則診斷小組在分析數據庫性能和應用服務器症狀時將無法看到網絡中的故障


黑盒核心應用程序或者它的某個依賴關系對於其內部可能不具有監控透明性JVM 是一個不折不扣的黑盒舉例來說診斷小組正在調查 JVM 中的莫名延時問題並且只擁有支持操作系統的統計數據(如 CPU 利用率和進程需要的內存大小)則他們可能無法診斷垃圾收集或線程同步問題


  脫節和斷開的監控系統應用程序可以由大型共享數據中心托管其中依賴關系由一系列共享資源組成比如說數據庫存儲區網絡(SAN)庫消息傳遞及中間件服務組織有時高度孤立各小組只負責管理自己的監控和 APM 系統沒有各依賴關系的整合視圖各組件所有者只能管中窺豹只見一斑

  孤立監控的缺陷

  當系統視圖無法反映整體情況時便會出現孤立 監控最為復雜和難以診斷的問題通常涉及多個參與和相關組件請考慮一個簡單的例子托管在 Java 應用服務器上的應用程序(所有者未知)實現了一個有故障的 JDBC 連接池類(洩漏連接)

  當應用程序所有者查看管理接口時他們聲稱自己的服務器與數據庫保持了 個連接相反數據庫管理員(DBA)查看數據庫管理控制台卻能看到相同的主機實際維持了 個連接並且其數量正在迅速增加經過整合的 APM 系統中創建一個顯示這兩種指標的曲線圖應該說是微不足道的當這兩個數字彼此背離時看到該圖的任何人都可以立即清楚地看到真實數字並能准確判斷出問題所在

  圖 對比了孤立和整合的 APM 系統

   孤立和整合 APM 系統的對比

  孤立和整合 APM 系統的對比


事後報告和相關性為嘗試解決孤立監控的問題運營支持小組可以運行定期進程獲取各來源的數據將這些數據整合到一個地方然後再生成匯總報表這種方法有時效率低下且不切實際因為它需要按照指定頻率嚴格執行而缺乏實時數據也會對診斷小組當場發現問題的能力產生負面影響此外事後聚合有時缺乏足夠的粒度從而導致重要模式隱藏在數據中不被發覺舉例來說某個報告可能顯示某特定服務調用昨天平均耗時 毫秒但卻隱藏了它在下午 :: 間平均耗時 毫秒


定期或隨需應變的監控由於某些工具強制占用較高的資源開銷因此不能(或不應)經常使用它們結果它們很少收集數據或者只在檢測到問題後才收集數據因此APM 系統只能執行最低基線而無法在問題惡化前提前報警並且可能會自己加劇勢態的嚴重性


非持久化監控許多工具都提供了有用的性能和可用性指標實時顯示功能但它們並不支持持久化指標供長期或短期比較和分析的功能常見的一種情況是如果缺少歷史上下文則性能指標將毫無價值因為沒有判斷指標優劣的基准舉例來說當前的 CPU 利用率是 %如果不知道歷史利用率的情況則不好判斷當前 CPU 利用率負荷的輕重程度但是如果知道歷史的典型值為百分之 x可接受的用戶性能上限是百分之 y則情況就大有改觀了


對生產前模型的依賴假設所有潛在問題都可在生產部署之前從環境中清除則完全依賴生產前監控和系統模型的實踐經常會導致運行時監控不夠全面這些假設無法解決不可預測事件和依賴性故障因此診斷小組在遇到此類事件時將沒有工具和數據可用

  整合 APM 的實現並不排除監控和診斷工具如 DBA 管理工具集低級網絡分析應用程序和數據中心管理解決方案這些工具仍然是無價的資源但如果它們依賴於整合視圖的專有性則難以克服孤立效果的影響

  理想 APM 系統的屬性

  與剛才討論的反面模式相反本系列文章介紹的理想 APM 系統擁有以下屬性


滲透力它監控所有應用程序組件和依賴關系
粒度化它可以監控層次極低的函數
整合性收集的所有指標將被發送到支持整合視圖的同一邏輯 APM 中
恆定一周 一天 小時不間斷監控
高效性能數據收集不會對監控目標造成不利影響
實時可以實時顯示報告和警告監控的資源指標
歷史監控的資源指標將持久化存儲在一個數據庫中因此可以查看比較和報告歷史數據

  在深入研究此系統的實現細節之前了解 APM 系統的一些基本概念是有幫助的

  APM 系統概念

  所有 APM 系統都能訪問性能數據源 並提供數據收集跟蹤 實用工具注意這些是我自己選擇的用於描述一般類別的通用術語它們並非特定於任何 APM 系統不同 APM 系統可以使用其他術語表示相同的概念在本文的其余部分中我所使用的術語定義如下

  性能數據源

  性能數據源(PDS)是性能或可用性數據的來源這些數據對於反映組件的相對健康狀況非常有用例如Java Management Extensions (JMX) 服務通常可以提供關於 JVM 健康狀況的豐富數據大多數關系數據庫通過 SQL 接口發布性能數據這兩種 PDS 都是直接 源的例子即可以直接提供性能數據相反推斷 源測定有意和偶然操作並且產生性能數據例如測試消息可以定期發送並隨後從 Java Message Service (JMS) 服務器中取回這個往返時間將作為該服務性能的推斷測量

  推斷源(它的實例被稱作綜合事務)有時極為有用因為它們可以通過遍歷與實際活動相同的路徑來有效測定多個組件或分層調用綜合事務還在監控連續性方面發揮著重要作用當直接源不能勝任時它們可以確認系統在相對空閒期的健康狀況

  收集和收集器

  收集是從 PDS 獲取性能或可用性數據的流程對於直接 PDS收集器 通常實現一些 API 來訪問該數據要從網絡路由器讀取統計數據收集器可以使用簡單網絡管理協議(Simple Network Management ProtocolSNMP)或 Telnet對於推斷 PDS收集器用於執行和測定底層操作

  跟蹤和跟蹤程序

  跟蹤是收集器向核心 APM 系統交付測量數據的流程許多商業和開源 APM 系統都提供了一些用於此目的的 API對於本文中的示例我實現了一個通用的 Java 跟蹤程序接口將在下節詳細討論

  通常大多數 APM 系統將跟蹤程序提交的數據組織到某種分類的層次結構中 展示了該數據捕獲的一般流程

   收集跟蹤和 APM 系統
收集、跟蹤和 APM 系統

  圖 還展示了 APM 系統提供的常用服務


實時顯示近乎實時顯示選定指標的圖表


報告生成的指標活動報告這通常包括一系列固定報告和自定義報告並能導出數據供用戶在別處使用


歷史庫包含原始或匯總指標的歷史數據庫從而能夠查看特定時間范圍內的圖表和報告


報警將收集指標確定的具體條件通知相關個體或組的功能典型的報警方法是電子郵件和某種類型的自定義鉤子接口可以允許運營小組將事件傳播給事件處理系統

  公共跟蹤 API 在 APM 的目標環境中的實現和應用提供了一些一致性此外自定義收集器的目的是讓開發人員能夠專心獲取性能數據而不必擔心跟蹤的問題下一節將介紹解決此問題的 APM 跟蹤接口

  ITracer跟蹤程序接口

  Java 語言可以很好地充當收集器的實現語言因為


廣泛的平台支持Java 收集器類可以在大多數目標平台上運行而無需修改這使監控架構可以在本地靈活地使用 PDS 合並收集器進程而不需要使用遠程收集


出色的性能(但是會隨可用資源而變化)


健壯的並發和同步執行支持


支持一組豐富的通信協議


受第三方 API 的廣泛支持比如 JDBC 實現SNMP 和專用 Java 接口因而能支持多種收集器庫


受活躍開源社區的支持它提供了額外的工具和接口使語言能訪問或獲取大量來源的數據

  但是有一點需要注意您的 Java 收集器必須能夠與目標 APM 系統提供的跟蹤 API 相結合如果您的 APM 跟蹤機制未提供 Java 接口則它的一些模式將仍然適用但是如果目標 PDS 只基於 Java(如 JMX)而應用程序平台並不基於 Java則需要一個橋接接口(如 IKVM)和一個 JavatoNET 編譯器

  當缺少官方標准時不同 APM 產品提供的跟蹤 API 也全然不同因此我通過實現一個通用的跟蹤 Java 接口(名稱為 orgruntimemonitoringtracingITracer)抽象了此問題ITracer 接口是針對專用跟蹤 API 的一個通用包裝器此技巧將確保源代碼庫不會因版本或 API 提供程序而有所不同並且還支持實現包裝 API 中不可用的額外功能本文中的大多數其余示例都實現了 ITracer 接口和它所支持的一般底層概念

  圖 orgruntimemonitoringtracingITracer 接口的 UML 類圖

   ITracer 接口和工廠類

  ITracer 接口和工廠類

  跟蹤類別和名稱

      ITracer 的基本前提是向中央 APM 系統提交一個度量和相關的名稱此活動由 trace 方法實現該方法因提交的度量而有所不同各 trace 方法都接受一個 String[] name 參數其中包含復合名稱的上下文組件其結構特定於 APM 系統復合名稱向 APM 系統指示提交的名稱空間和實際的指標名稱因此復合名稱中通常至少包括根類別和度量說明底層 ITracer 實現應該知道如何通過傳遞的 String[] 構建復合名稱 演示了復合命名約定的兩個示例

   示例復合名稱
名稱結構 復合名稱 簡單斜槓分隔 Hosts/SalesDatabaseServer/CPU Utilization/CPU JMX MBean ObjectName commycodatacenterapm:type=Hostsservice=SalesDatabaseServergroup=CPU Utilizationinstance=CPU

  清單 是使用此 API 跟蹤調用的簡短示例

  清單 跟蹤 API 調用示例

  ITracer simpleTracer = TracerFactorygetInstance(sprops); ITracer jmxTracer = TracerFactorygetInstance(jprops); simpleTracertrace( Hosts SalesDatabaseServer CPU Utilization CPU Current Utilization %); jmxTracertrace( commycodatacenterapm type=Hosts service=SalesDatabaseServer group=CPU Utilization instance=CPU Current Utilization %); );

  跟蹤程序度量數據類型

  在此接口中度量數據可以是以下類型


int
long
javautilDate
String

  APM 系統提供商可能支持其他數據類型的收集度量數據

  跟蹤程序類型

  選定了具體的度量數據類型(如 long)之後可以根據 APM 系統支持的類型來選擇解釋特定值的方式還需記住各 APM 實現可以使用不同的術語來表示本質相同的類型並且 ITracer 使用了一些通用的命名規則

  ITracer 中表示的跟蹤程序類型


平均時間間隔trace(long value String[] name) 和 trace(int value String[] name) 方法將發出時間間隔平均值的跟蹤(請參閱 時間間隔)這表示每個提交將被轉化為當前時間間隔的聚合值當新時間間隔開始時聚合值計數器將重置為零


粘附 traceSticky(value long String[] name)traceSticky(value int String[] name) 方法發出粘附值跟蹤這表示與時間間隔平均指標相反聚合將它們的值保留在時間間隔中如果現在跟蹤值 而此後不再執行跟蹤直到第二天某個時刻則該指標將保持為 直到提供了新值


增量增量跟蹤將傳遞一個數值但提供給 APM 系統(或由 APM 系統解釋)的實際值是此度量與前一度量之間的增量它們有時被稱作 rate 類型用於反映自己的性能優勢請考慮事務管理程序的提交總數度量值該數字始終在增加並且其絕對值幾乎沒有用處該數字有用的地方是它增加的速率因此定期收集它的絕對值並跟蹤每次讀取數據之間的增量可以反映事務提交的速率增量跟蹤比平均時間間隔和粘附方式的跟蹤更為常用但仍有些用例采用了平均時間間隔增量跟蹤必須能夠區分只能增加的度量和同時能增減的度量小於前值的提交度量應被忽略或造成底層增量重置


事件這種類型是一種簡單的非聚合指標它表示特定事件在時間間隔內發生的次數的增量計算由於收集器和跟蹤程序都不期望知道特定時刻的運行總數因此基本的 traceIncident(String[] name) 調用沒有指定任何值並且隱式只增加一次事件增量當需要計算多次增量時除了在循環中多次調用該方法之外另一種較好的方法是通過 traceIncident(int value String[] name) 方法根據 value 來計算合值


智能智能跟蹤程序是一個參數化的類型它與跟蹤程序中的某種其他類型相映射度量值和跟蹤類型將作為 String 傳遞並且可將可用類型作為常量定義在接口中當收集器不知道正在收集的數據的類型或跟蹤程序類型時這是一個非常方便的方法但是也可以直接將收集值和配置的類型名稱傳遞給跟蹤程序

  時間間隔

  討論跟蹤程序類型需要了解時間間隔 的概念請考慮這樣一個概念進程正在收集操作已經運行的時間並將它發送給 APM 系統您每分鐘都可以看到成百上千的調用傳輸並存儲每個度量的細節並不是切實有效的方法而且也不應該在任何性能報告或圖表中反應每一個度量因為即便非常罕見的調用也可以歪曲整個信息表示同時由於長時期過度捕獲您將喪失粒度化的概念因為這段時間內的合法峰值可能相當多

  解決此問題的一種模式是選取一個時間時隔來表示您希望聚合的最低粒度 個小時可能太長 毫秒又太短因此您可以設定 秒的間隔現在您仍然可以對假設操作的每次調用都調用 trace但是保留在各時間間隔末尾的數據是


時間間隔平均耗時
時間間隔最長耗時
時間間隔最短耗時
時間間隔內的調用次數
時間時隔開始時刻的時間戳

  這是過度聚合度量和存儲所有單獨度量的一種有效的折衷方法

  TracerFactory 是一個普通的工廠類用於根據傳遞的配置屬性創建新 ITracer 實例或者從緩存中引用已創建的 ITracer

  收集器模式

  收集通常有三種可選模式這影響到應該使用的跟蹤程序類型


輪詢按固定頻繁調用收集器它將檢索和跟蹤 PDS 中的指標或指標集的當前值例如可以每分鐘調用一次收集器來讀取主機的 CPU 利用率或通過 JXM 接口從事務管理器讀取提交事務的總數輪詢模式的前提是對目標指標的定期采樣因此對於輪詢事件指標的值將提供給 APM 系統但是假定中間時期的值不變因而輪詢收集器通常使用粘附跟蹤程序類型APM 系統在生成報告時將假定所有輪詢事件之間的值不變 演示了此模式


輪詢收集模式
輪詢收集模式


監聽這種通用數據模式是 Observer 模式的一種形式收集器將其自身注冊為目標 PDS 的事件監聽程序它將在相關的事件發生時接受回調作為回調結果發出的跟蹤值取決於回調有效負荷本身的內容但收集器至少可以跟蹤每個回調的事件 演示了此模式

監聽收集模式
監聽收集模式



截取在此模式中收集器將自己作為截取程序插入到目標和它的調用程序之間對於通過該截取程序的各個活動實例截取程序將生成一個度量並跟蹤它當截取模式是 request/response收集器可以測定請求數量響應時間請求或響應的有效負荷例如HTTP 代碼服務器可以充當收集器它可以
計算請求數可以選擇根據 HTTP 類型(GETPOST 等)或統一資源標識符(URI)來分類
請求的響應時間
測定請求和響應的大小 由於您可以假定截取收集器能 看到 每一個事件因此實現的跟蹤程序通常為平均時間間隔類型因此如果時間間隔到期且沒有活動發生則該時間間隔的聚合值將為零而與之前時間間隔中的活動無關 演示了此模式

截取收集模式
截取收集模式

  現在我已經介紹了性能數據跟蹤 API它的底層數據類型和數據收集的模式接下來我將通過一些用例和示例來演示 API 的應用

  監控 JVM

  從 JVM 開始實現性能監控是個明智的選擇首先我將介紹所有 JVM 共同的性能指標然後再介紹企業給應用程序中經常使用的一些 JVM 駐留組件通常Java 應用程序實例是受底層操作系統支持的進程因此JVM 監控的某些方面最好是從主機 OS 的視角來理解這些內容將在第 部分中介紹

  在Java Platform Standard Edition (Java SE) 發行之前能夠在運行時有效和可靠收集的內部及標准化 JVM 診斷信息非常有限現在javalangmanagement 接口提供了一些有用的監控點該接口是所有兼容 Java SE (和更新版本)的 JVM 版本的標准這些 JVM 的某些實現提供了額外的屬性指標但是它們的訪問模式卻基本相同我將重點介紹可以通過 JVM 的 MXBeans 訪問的標准模式 — 部署在 VM 內部的 JMX MBeans 公開了一個管理和監控接口(請參閱 參考資料)


ClassLoadingMXBean監控類加載系統
CompilationMXBean監控編譯系統
GarbageCollectionMXBean監控 JVM 的垃圾收集器
MemoryMXBean監控 JVM 的堆和非堆內存空間
MemoryPoolMXBean監控 JVM 分配的內存池
RuntimeMXBean監控運行時系統該 MXBean 提供的有用監控指標很少但它確實提供了 JVM 的輸入參數和啟動時間及運行時間這兩者在其他派生指標中都是很有用的
ThreadMXBean監控線程系統

  JMX 收集器的前提是它將獲取一個 MBeanServerConnection 對象該對象可以讀取部署在 JVM 中的 MBeans 的屬性讀取目標屬性的值並使用 ITracer API 跟蹤它們對於這種類型的收集決定部署收集器的位置非常關鍵可行的選擇包括本地部署遠程部署

   權限問題

  本地和遠程部署都可能會受到各種配置權限的限制這些權限會阻止收集器訪問 JVM 數據這些問題大多都有相應的解決方法但是它們的多變性使本文無法詳細介紹這方面的內容

  在本地部署中收集器和它的調用調度程序部署在目標 JVM 中隨後JMX 收集器組件將使用 PlatformMBeanServer(可以通過 JVM 內部的 MBeanServerConnection 來連接它)訪問 MXBeans在遠程部署中收集器運行在一個單獨的進程中並使用某種形式的 JMX Remoting 來連接目標 JVM這可能沒有本地部署那麼高效但它不需要在目標系統中部署任何額外的組件JMX Remoting 不在本文的討論范圍之內但它的實現方法非常簡單部署一個 RMIConnectorServer 或在 JVM 中啟用外部連接(請參閱 參考資料)

  示例 JMX 收集器

  本文的示例 JMX 收集器(請閱讀 下載 一節獲取本文的完整源代碼)包含三個單獨的方法可用於獲取 MBeanServerConnection該收集器可以


通過調用靜態 javalangmanagementManagementFactorygetPlatformMBeanServer() 方法為本地 JVM 的平台 MBeanServer 獲取一個 MBeanServerConnection


通過調用靜態 javaxmanagementMBeanServerFactoryfindMBeanServer(String agentId) 方法為部署在本地 JVM 平台中的備用 MBeanServer 獲取一個 MBeanServerConnection注意一個 JVM 中可以存在多個 MBeanServer並且Java Platform Enterprise Edition (Java EE) 服務器等較為復雜的系統幾乎始終擁有特定於應用服務器的 MBeanServer它是獨立的且有別於平台 MBeanServer(請參閱 交叉注冊 MBeans 側邊欄)


使用 javaxmanagementremoteJMXServiceURL 通過標准 RMI Remoting 獲取一個遠程 MBeanServerConnection

  清單 是摘錄自 JMXCollector collect() 方法的代碼段它顯示了 ThreadMXBean 中的收集和線程跟蹤活動點擊 此處 查看完整清單

  清單 示例 JMX 收集器的 collect() 方法的部分代碼它使用 ThreadMXBean

   objectNameCacheput(THREAD_MXBEAN_NAME new ObjectName(THREAD_MXBEAN_NAME)); public void collect() { CompositeData compositeData = null; String type = null; try { log(Starting JMX Collection); long start = SystemcurrentTimeMillis(); ObjectName on = null; // Thread Monitoring on = objectNameCacheget(THREAD_MXBEAN_NAME); tracertraceDeltaSticky((Long)jmxServergetAttribute(onTotalStartedThreadCount) hostName JMX ongetKeyProperty(type) StartedThreadRate); tracertraceSticky((Integer)jmxServergetAttribute(on ThreadCount) hostName JMX ongetKeyProperty(type) CurrentThreadCount); // Done long elapsed = SystemcurrentTimeMillis()start; tracertrace(elapsed hostName JMX JMX Collector Collection Last Elapsed Time); tracertrace(new Date() hostName JMX JMX Collector Collection Last Collection); log(Completed JMX Collection in elapsed ms); } catch (Exception e) { log(Failed: + e); tracertraceIncident(hostName JMX JMX Collector Collection Collection Errors); } }


  清單 中的代碼將跟蹤 TotalThreadsStartedCurrentThreadCount 的值由於它是輪詢收集器因此兩個跟蹤都使用粘附選項但是由於 TotalThreadsStarted 是一個不斷增加的數值因此最吸引人的地方不是絕對值而是已創建線程的速率這樣該跟蹤程序將使用 DeltaSticky 選項

  圖 顯示了此收集器創建的 APM 指標樹

   JMX 收集器 APM 指標樹
JMX 收集器 APM 指標樹

  JMX 收集器的一些方面並未顯示在清單 比如說調度注冊它將每隔 分鐘為 collect() 方法創建一個定期回調

  在清單 不同跟蹤程序類型和數據類型的實現方法將由數據源決定例如


TotalLoadedClassesUnloadedClassCount 將作為粘附增量被跟蹤因為它們的值始終遞增而且增量在測定類加載活動方面比絕對值更加有用


ThreadCount 變量可增加或減少因此它將作為粘附類型被跟蹤


收集錯誤 將作為內部事件被跟蹤它將在收集遇到異常時遞增

  為了追求效率由於目標 MXBeans 的 JMX ObjectName 在目標 JVM 的生存期不會更改因此收集器使用 ManagementFactory 常量名來緩存名稱

  對於 MXBeans 的兩種類型 — GarbageCollector 和 MemoryPool — 准確的 ObjectName 無法預先知曉但是您可以提供一個通用的模式在這些情況下在初次執行收集時您將對 MBeanServerConnection 發起一個查詢並請求與提供模式相匹配的所有 MBeans 的列表為避免未來在目標 JVM 的生存期執行查詢返回的匹配 MBean ObjectName 將緩存在內存中

  在某些情況下收集的目標 MBean 屬性可能不是純數值類型MemoryMXBean 和 MemoryPoolMXBean 就是這種情況對於這些情況屬性類型是可查詢鍵和值的 CompositeData 對象對於 javalangmanagement JVM 管理接口MXBean 標准采用了 JMX Open Types 模型在該模型中所有屬性都是語言無關的類型如 javalangBoolean 和 javalangInteger或者對於 javaxmanagementopenmbeanCompositeType 等復雜類型這些類型可以被分解為相同簡單類型的鍵/值對簡單類型的完整列表枚舉在靜態 javaxmanagementopenmbeanOpenTypeALLOWED_CLASSNAMES 字段中該模型支持一個類型獨立層使 JMX 客戶機不用依賴於非標准的類並且還可以支持非 Java 客戶機因為底層類型相對比較簡單

  對於目標 MBean 屬性是非標准復雜類型的情況您需要確保定義該類型的類在收集器的類路徑中並且您必須實現一些自定義代碼來呈現檢索到的復雜對象中的有用數據

  如果獲取了單個連接並為所有收集保留了該連接則需要通過錯誤檢測和修復來創建一個新連接以防止該連接出現故障某些收集 API 提供斷開監控程序可以提示收集器關閉消除和創建新連接如果收集器嘗試連接到由於維護而停機或由於其他原因而無法訪問的 PDS則收集器應該以合適的頻率輪詢並重新連接跟蹤連接的運行時間還可用於在檢測到關機時減少收集的頻率這可以減少已超負荷運行了一段時間的目標 JVM 的開銷

  這些示例中未實現的兩個額外技巧可以改進 JMX 收集器的效率並減少它在目標 JVM 中運行所需的開銷第一個技巧適用於從一個 MBean 中查詢多個屬性的情況借助 getAttributes(ObjectName name String[] attributes)您可以在一個調用中請求多個屬性而不必使用 getAttribute(ObjectName name String attribute) 一次請求一個屬性這種差異在本地收集中可以忽略但是在遠程收集中卻可以顯著減少資源的使用因為它可以減少網絡調用的數量第二個技巧是使用監控收集模式代替輪詢模式從而進一步減少 JMX 公開內存池的輪詢開銷MemoryPoolMXBean 支持建立一個使用率閥值超過該閥值時將觸發向監控程序發送一個通知而監控程序將跟蹤該值當內存使用率增加時使用率閥值可以相應地增加這種方法是缺陷是如果使用率閥值沒有微小的增量則一些粒度級的數據可能會丟失並且閥值下方的內存使用率模式將變為不可見

  最後一個未實現的技巧是測定運行時間和垃圾收集總運行時間的范圍並實現一些簡單的算法來計算垃圾收集器處於活動狀態的時間在已運行時間中的百分比這是一個有用的指標因為一些垃圾收集器(當前)是大多數應用程序必須要面對的問題由於某些收集(分別執行了一段時間)是期望執行的因此運行垃圾收集占用的時間可以更加清楚地反映 JVM 的內存健康狀況根據經驗(因應用程序而大不相同)占用任何 分鐘時間段內的 % 以上則表示存在潛在問題

  收集器的外部配置

  為便於演示收集流程本文介紹的 JMX 收集器經過了適當簡化但它僅限於硬編碼的收集方式理想情況下收集器將實現數據訪問方式而外部提供的配置將提供內容這種設計使收集器更具實用性且易於重用對於最高級別的重用外部配置的收集器應該支持這些配置點


PDS 連接工廠指令為收集器提供用於連接到 PDS 的接口以及在連接時使用的配置
執行收集的頻率
嘗試重新連接的頻率
收集的目標 MBean或通配符形式的對象名稱
對於各目標跟蹤復合名稱或者應該跟蹤的度量片段以及應該跟蹤的數據類型

  清單 演示了 JMX 收集器的外部配置

  清單 JMX 收集器的外部配置示例

  <?xml version= encoding=UTF?> <JMXCollector> <attribute name=ConnectionFactoryClassName> collectorsjmxRemoteRMIMBeanServerConnectionFactory </attribute> <attribute name=ConnectionFactoryProperties> jmxrmiurl=service:jmx:rmi:///jndi/rmi://:/jmxconnector </attribute> <attribute name=NamePrefix>JMX</attribute> <attribute name=PollFrequency></attribute> <attribute name=TargetAttributes> <TargetAttributes> <TargetAttribute objectName=javalang:type=Threading attributeName=ThreadCount Category=Threading metricName=ThreadCount type=SINT/> <TargetAttribute objectName=javalang:type=Compilation attributeName=TotalCompilationTime Category=Compilation metricName=TotalCompilationTime type=SDINT/> </TargetAttributes> </attribute> </JMXCollector>


  注意TargetAttribute 元素包含一個名為 type 的屬性它表示智能類型跟蹤程序的參數化變量SINT 類型表示粘附 intSDINT 類型表示增量粘附 int

  通過 JMX 監控應用程序資源

  目前為止我已經討論了通過 JMX 監控惟一標准的 JVM 資源但是許多應用程序架構如 Java EE可以通過 JMX 公開重要的特定於應用程序的指標(這取決於供應商)一個典型的例子是 DataSource 利用率DataSource 是一個用於將連接池化到外部資源(通常為數據庫)的服務這限制了並發連接的數量以保護資源不受惡意應用程序的占用監控數據源是整個監控計劃中的關鍵環節由於 JMX 抽象層該流程與之前介紹的類似

  下面是來自 JBoss 應用服務器實例的典型數據源指標


可用連接數當前池中可用連接的數量
連接數連接池與數據庫建立的實際物理連接的數量
最大使用連接數池中正在使用的連接的上限標記
正在使用的連接數當前正在使用的連接數量
已創建的連接數為該池創建的連接總數
已部署的連接數為該池部署的連接總數

  現在收集器將使用批屬性檢索並在一個調用中獲取所有屬性惟一需要注意的是您需要查詢返回的數據以接通不同的數據和跟蹤程序類型DataSource 指標在沒有活動時也是不會變化的因此要使數值變化您需要生成一些負載清單 顯示 DataSource 收集器的 collect() 方法

  清單 DataSource 收集器

  public void collect() { try { log(Starting DataSource Collection); long start = SystemcurrentTimeMillis(); ObjectName on = objectNameCacheget(DS_OBJ_NAME); AttributeList attributes = jmxServergetAttributes(on new String[]{ AvailableConnectionCount MaxConnectionsInUseCount InUseConnectionCount ConnectionCount ConnectionCreatedCount ConnectionDestroyedCount }); for(Attribute attribute: (List<Attribute>)attributes) { if(attributegetName()equals(ConnectionCreatedCount) || attributegetName()equals(ConnectionDestroyedCount)) { tracertraceDeltaSticky((Integer)attributegetValue() hostName DataSource ongetKeyProperty(name) attributegetName()); } else { if(attributegetValue() instanceof Long) { tracertraceSticky((Long)attributegetValue() hostName DataSource ongetKeyProperty(name) attributegetName()); } else { tracertraceSticky((Integer)attributegetValue() hostName DataSourceongetKeyProperty(name) attributegetName()); } } } // Done long elapsed = SystemcurrentTimeMillis()start; tracertrace(elapsed hostName DataSource DataSource Collector Collection Last Elapsed Time); tracertrace(new Date() hostName DataSource DataSource Collector Collection Last Collection); log(Completed DataSource Collection in elapsed ms); } catch (Exception e) { log(Failed: + e); tracertraceIncident(hostName DataSource DataSource Collector Collection Collection Errors); } }


  圖 顯示了 DataSource 收集器的相應指標樹

   DataSource 收集器指標樹

  DataSource 收集器指標樹

  監控 JVM 中的組件

   交叉注冊 MBeans

  在許多情況下可以將目標 MBeans 注冊到相同 JVM 的不同 MBeanServer例如javalang MXBean 注冊在平台代理(也稱作 JVM MBeanServer)中而 JBoss 服務器中的 DataSource MBeans 位於 jboss MBeanServer在遠程監控實現中這種情況有時會增加額外的開銷和配置復雜度因為每個 MBeanServer 都需要兩個遠程連接此外為遠程連接公開平台 MBeanServer 也會帶來額外的開銷在這些情況下通常采用交叉注冊 MBeans 的方式這樣所有的目標 MBeans 都可以通過相同的 MBeanServer 接口來監控請查閱本文的 源代碼獲取名稱為 mapplatformmxbeansbsh 的示例 beanshell 腳本如果您將此腳本部署到 JBoss 服務器中它將在 JBoss MBeanServer 中交叉注冊平台 MBeanServer 的 MXBeans

  本節介紹的技巧可用於監控應用程序組件服務類和方法相關的主要區域如下


調用速率調用服務或方法的速率


調用響應速率服務或方法響應的速率


調用錯誤率服務或方法生成錯誤的比率


調用運行時間調用在每個間隔時間內的平均最短和最長運行時間


調用並發性並發調用服務或方法時執行的線程數

  使用 Java SE (和更新版本)ThreadMXBean 的一些實現提供的指標還可以收集以下指標


系統和用戶 CPU 時間調用某方法占用的 CPU 時間


等待數量和總等待時間調用某方法或服務時等待線程的實例數量和總占用時間當線程進入 WAITINGTIMED_WAITING 等待狀態並暫停另一個線程的活動時將發生等待事件


阻塞數量和總阻塞時間在調用某個方法或服務時處於 BLOCKED 狀態的線程的實例數量和總占用時間當線程等待監控鎖進入或重新進入同步阻塞時會發生阻塞事件

  還可以使用備選工具集和本機接口來確定這些指標和其他指標但這通常涉及某種級別的開銷從而造成不必要的生產運行時監控已經說過指標本身甚至在收集時是低級的它們的作用也許僅限於分析趨勢並且很難與無法通過其他手段確定的因果效應相關聯

  所有上述指標都可以通過插裝類和方法的流程來收集以便於收集和跟蹤目標 APM 系統的性能數據可以采用各種技巧來直接插裝 Java 類或者通過它們來間接計算性能指標


源代碼插裝最基本的技巧是在源代碼階段添加插裝這樣編譯和部署後的類就已經在運行時包含了插裝在某些情況下這種方法具有意義並且一些特定的實踐使它成為可行的流程和投資


截取通過截取程序(執行測定和跟蹤)轉移調用可以實現有效和准確的跟蹤而無需接觸目標類它們的源代碼和運行時字節碼這種實踐簡單可取因為存在許多 Java EE 框架和其他流行的 Java 框架
通過配置支持抽象
支持類注入和通過接口引用
有某些情況下直接支持截取棧概念執行流程經過定義了配置的對象棧其作用是接收調用並執行一些處理然後繼續傳遞

字節碼插裝該流程將字節碼注入到應用程序類中注入的字節碼將添加性能數據收集插裝該插裝被作為新類的一部分調用這個流程有時極為有效因為插裝是完全經過編譯的字節碼並且代碼的執行路徑以最細化的方式擴展同時仍然能夠收集數據它的另一個優點是無需修改初始源代碼並且其對環境的配置更改也可能最少此外通用模式和字節碼注入技巧允許對源代碼不可用的類和庫進行插裝許多第三方類屬於這種情況


類包裝該流程使用另一個類來包裝或替換目標類前者實現了相同功能同時也包含了插裝

  在本文的第 部分中我只討論基於源代碼的插裝您將在第 部分中了解更多關於截取字節碼插裝和類包裝的信息(從拓撲學的角度來說截取字節碼插裝和類包裝的本質完全相同但它們實現結果的操作有稍微不同的含義)

  異步插裝

  異步插裝是類插裝中的基本問題上一節討論了輪詢性能數據的概念如果輪詢完成得足夠好則它應該不會對核心應用程序性能或開銷造成影響相反插裝應用程序代碼本身會直接修改和影響核心代碼的執行任何插裝的目標都必須是無論如何不產生危害開銷損失必須盡可能接近忽略不計事實上消除測量本身中的極細微的損失是不可能的但是在獲取性能數據之後保持其余跟蹤進程異步是非常重要的可以采用若干種模式來實現異步跟蹤 演示了異步跟蹤的實現方法概覽

   異步跟蹤
異步跟蹤

  圖 演示了一個簡單的插裝截取程序它通過捕獲調用的起始時間來測量它的運行時間然後將測量數據(運行時間和指標復合名稱)分發給處理隊列然後線程池讀取該隊列獲取測量數據並完成跟蹤流程

  源代碼中的 Java 類插裝

  本節將討論如何實現源代碼級插裝並將提供一些最佳實踐和示例代碼文章還介紹了一些新的跟蹤結構我將在源代碼插裝的上下文中闡明它們的操作和它們的插裝模式

  雖然其他選擇已經流行但源代碼插裝在某些實例中是無法避免的在某些情況下它是惟一的解決方案借助一些明智的預防措施它可以實現良好的效果需要考慮的事項包括


如果插裝代碼的方案可用並且無法實現配置更改來垂直影響插裝則使用可配置和靈活的跟蹤 API 非常重要


抽象的跟蹤 API 類似於事件記錄 API(如 logj)它們的共用屬性包括
運行時冗長控制logj 記錄程序和附加程序的冗長等級可以在系統啟動時配置並隨後在運行時修改同樣跟蹤 API 應該能夠根據分級名稱模式來控制哪些指標名稱受跟蹤支持


輸出端點配置logj 通過記錄程序發起記錄聲明並將它分發給附加程序經過配置附加程序可以將記錄流發送給各種輸出如文件套接字和電子郵件跟蹤 API 不需要多樣的輸出方式但抽象專有或特定於 APM 系統的庫的能力將保護源代碼不受外部配置的更改
在某些情況下通過其他方法來跟蹤具體的項目不太可行通常我將這種情況稱作上下文跟蹤我使用該術語描述的性能數據並不是很重要但它為主要數據添加上下文

  上下文跟蹤

  上下文跟蹤受具體的應用程序影響極大但是可以考慮一個經過簡化的例子含有 processPayroll(long clientId) 方法的 payrollprocessing 類當被調用時該方法計算並存儲各客戶員工的薪水您可以通過各種方法插裝該方法但是執行中的底層模式清楚表明調用時間的增加與員工的數量不成比例因此研究 processPayroll 的運行時間趨勢沒有上下文可供參考除非您知道程序每次處理的員工數量簡單來講對於特定的時間段processPayroll 平均耗時 x 毫秒無法確定這個值反映的性能是好還是壞因為您不知道它處理的員工數量是 還是 而兩種情況反映的性能差別巨大清單 在代碼中顯示了這個簡化的概念

  清單 上下文跟蹤的例子

  public void processPayroll(long clientId) { Collection<Employee> employees = null; // Acquire the collection of employees // // // Process each employee for(Employee emp: employees) { processEmployee(empgetEmployeeId() clientId); } }


  此處的主要挑戰是根據大多數插裝技巧processPayroll() 方法中的任何東西都是不可觸及的因此雖然能夠插裝 processPayroll 甚至 processEmployee但是卻無法跟蹤員工的數量從而不能為方法的性能數據提供上下文清單 顯示了一個拙劣的硬編碼示例(且有點效率不高)它將捕獲上面提到的上下文數據

  清單 上下文跟蹤示例

  public void processPayrollContextual(long clientId) { Collection<Employee> employees = null; // Acquire the collection of employees employees = popEmployees(); // Process each employee int empCount = ; String rangeName = null; long start = SystemcurrentTimeMillis(); for(Employee emp: employees) { processEmployee(empgetEmployeeId() clientId); empCount++; } rangeName = tracerlookupRange(Payroll Processing empCount); long elapsed = SystemcurrentTimeMillis()start; tracertrace(elapsed Payroll Processing rangeName Elapsed Time (ms)); tracertraceIncident(Payroll Processing rangeName Payrolls Processed); log(Processed Client with + empCount + employees); }


  清單 中的關鍵部分是 tracerlookupRange 調用Ranges 是指定的收集它由數值范圍限制鍵控並且擁有一個表示數值范圍名稱的 String不再跟蹤薪水處理的簡單無格式運行時間清單 將員工計數劃分為范圍有效分隔運行時間並根據基本類似的員工計數將它們分組 顯示了 APM 系統生成的指標樹

  根據范圍分組的薪水處理時間
根據范圍分組的薪水處理時間

  圖 演示了根據員工計數劃分的薪水處理運行的時間它揭示了員工數量和運行時間之間的相互關系

   各范圍的薪水處理運行時間
各范圍的薪水處理運行時間

  跟蹤程序配置屬性允許在屬性文件中包括 URL並能在其中定義范圍和閥值(我將簡單介紹一下閥值)屬性將在跟蹤程序的構造時間被讀取並為 tracerlookupRange 實現提供後台數據清單 顯示了 Payroll Processing 范圍的示例配置我選擇使用 javautilProperties 的 XML 表示因為它更能兼容奇怪的字符

  清單 范圍配置示例

  <?xml version= encoding=UTF?> <!DOCTYPE properties SYSTEM > <properties> <comment>Payroll Process Range</comment> <entry key=L:Payroll Processing>+ Emps: Emps: Emps : Emps: Emps: Emps</entry> </properties>


  注入外部定義的范圍可以使您的應用程序不必頻繁更新源代碼這受益於預期的調整和服務水平協議(SLA)在業務方面的變更當范圍和閥值更改生效之後您只需更新外部文件而不是應用程序本身

  跟蹤閥值和 SLA

  外部可配置上下文跟蹤的靈活性支持以更加准確和粒度化的方式來定義和測量性能閥值范圍 定義一系列數值區間可以在其中對測量數據進行分類閥值 是對范圍的進一步分類它根據測量數據的確定范圍對獲取的測量數據進行分類在分析收集的性能數據時一個常見的需求是確定和報告執行是 成功 還是 失敗(因為它們未在指定時間發生)這些數據的總和可以作為關於系統運行健康狀況和性能的通用成績單或者作為某種形式的 SLA 遵從性評價

  使用薪水處理系統示例考慮一個內部服務級目標它將薪水的執行時間(在定義的員工數范圍之內)定義為 OkWarnCritical 個區間生成閥值計數的流程從概念上來說非常簡單您只需為跟蹤程序提供您認為是各類別各區間的上限運行時間的值並引導跟蹤程序為分類的運行時間發起一個 tracertraceIncident然後 — 為簡化報告 — 提供一個總數 顯示了一些經過設計的 SLA 運行時間

   薪水處理閥值
員工數 Ok (ms) Warn (ms) Critical (ms) > > > > > + >

      ITracer API 使用與范圍中相同的 XML(屬性)文件中定義的值實現了閥值報告范圍和閥值定義在兩個方面稍有不同首先閥值定義的關鍵值是一個正則表達式ITracer 在跟蹤一個數值時它會檢查閥值正則表達式是否匹配被跟蹤指標的復合名稱如果匹配則閥值會將測量數據分類為 OkWarnCritical並為跟蹤附加一個額外的 tracertraceIncident其次由於閥值只定義了兩個值(根據定義Critical 值大於 warn 值)因此配置只由兩個數值組成清單 顯示了之前介紹的薪水處理 SLA 的閥值配置

  清單 薪水處理的閥值配置

  <?xml version= encoding=UTF?> <!DOCTYPE properties SYSTEM > <properties> <! Payroll Processing Thresholds > <entry key=Payroll Processing* Emps*Elapsed Time \(ms\)></entry> <entry key=Payroll Processing* Emps*Elapsed Time \(ms\)></entry> <entry key=Payroll Processing* Emps*Elapsed Time \(ms\)></entry> <entry key=Payroll Processing* Emps*Elapsed Time \(ms\)></entry> <entry key=Payroll Processing* Emps*Elapsed Time \(ms\)></entry> <entry key=Payroll Processing*\+ Emps*Elapsed Time \(ms\)></entry> </properties>


  圖 顯示添加了閥值指標的薪水處理的指標樹

   添加了閥值的薪水處理指標
添加了閥值的薪水處理指標

  圖 演示了哪些收集的數據可以表示在餅形圖中

   薪水處理的 SLA 匯總( 名員工)
薪水處理的 SLA 匯總

  確保查找上下文和閥值分類的效率和速度非常重要因為它們在完成實際工作的線程中執行在 ITracer 實現中所有指標名稱在第一次被跟蹤程序發現時將存儲在(線程安全)為具備和不具備閥值的指標指定的映射中當特定指標的跟蹤事件發生後閥值確定過程占用的時間是一個 Map 查找時間它的速度通常足夠快如果閥值條目或指標名稱的數量非常大則一種合理的解決方案是推遲閥值確定並在異步跟蹤線程池中處理它們

  第 部分結束語

  本系列文章的第 部分介紹了一些監控反面模式和一些 APM 系統需要的屬性我總結了一些通用性能數據收集模式並介紹了 ITracer 接口我將在本系列文章的其余部分繼續使用它我已經演示了監控 JVM 健康狀況的技巧以及如何通過 JMX 獲取通用性能數據最後我總結了各種實現高效和防代碼更改的源代碼級插裝方法(用於監控原始性能統計數據和上下文派生統計數據)以及如何使用這些統計數據生成關於應用程序 SLA 的報告 部分將探究插裝 Java 系統而無需修改應用程序源代碼的技巧具體方法是使用截取類包裝和動態字節碼插裝


From:http://tw.wingwit.com/Article/program/Java/hx/201311/25657.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.