用 JMX 检测应用程序
有多少次您曾經(jīng)注視著運(yùn)行中的應(yīng)用程序,問自己:“它到底在做什么?為什么用了這么長時(shí)間呢?” 在這些時(shí)刻,您可能會(huì)想如果自己在應(yīng)用程序中構(gòu)建了更多的監(jiān)視功能就好了。例如,在服務(wù)器應(yīng)用程序中,能夠查看排隊(duì)等候處理的任務(wù)的數(shù)量和類型、當(dāng)前正在處理的任務(wù)、過去一分鐘或一小時(shí)內(nèi)的吞吐量統(tǒng)計(jì)、平均任務(wù)處理時(shí)間等。這些統(tǒng)計(jì)值容易搜集,但是在需要數(shù)據(jù)的時(shí)候,如果沒有非侵入性的數(shù)據(jù)檢索機(jī)制,那么這些值就不太有用。
可以用許多方式導(dǎo)出操作性數(shù)據(jù)——可以把周期性統(tǒng)計(jì)快照寫入日志文件、創(chuàng)建 Swing GUI、使用內(nèi)嵌的 HTTP 服務(wù)器在 Web 頁面上顯示統(tǒng)計(jì)值或者發(fā)布可以用來查詢應(yīng)用程序的 Web 服務(wù)。但是在缺少監(jiān)視和數(shù)據(jù)發(fā)布基礎(chǔ)設(shè)施的情況下,多數(shù)應(yīng)用程序開發(fā)人員都做不到這些,因此造成對(duì)應(yīng)用程序工作情況的了解要比預(yù)期的少很多。
JMX
在 Java 5.0 中,類庫和和 JVM 提供了一種全面的管理和監(jiān)視基礎(chǔ)設(shè)施——JMX。JMX 是一種用來提供可以遠(yuǎn)程訪問的管理接口的標(biāo)準(zhǔn)措施,也是一種向應(yīng)用程序添加靈活且強(qiáng)大的管理接口的簡易方式。被稱作受管 bean(MBean)的 JMX 組件,是提供與實(shí)體的管理有關(guān)的訪問器和業(yè)務(wù)方法的 JavaBean。每個(gè)受管的實(shí)體(可能是整個(gè)應(yīng)用程序或應(yīng)用程序中的服務(wù))實(shí)例化一個(gè) MBean 并用可讀懂的名稱注冊(cè)它。支持 JMX 的應(yīng)用程序依賴于?MBeanServer,它充當(dāng) MBean 的容器,提供遠(yuǎn)程訪問、命名空間管理和安全服務(wù)。在客戶端,jconsole?工具可以充當(dāng)統(tǒng)一的 JMX 客戶機(jī)。結(jié)合兩者,對(duì) JMX 的平臺(tái)支持極大地降低了使應(yīng)用程序支持外部管理接口所需的工作和努力。
除了提供?MBeanServer?實(shí)現(xiàn),Java SE 5.0 還提供 JVM 以更方便地了解內(nèi)存管理、類裝入、活動(dòng)線程、日志和平臺(tái)配置的狀態(tài)。多數(shù)平臺(tái)服務(wù)的監(jiān)視和管理在默認(rèn)情況下都是開啟的(性能影響最小),所以只需要連接應(yīng)用程序與 JMX 客戶機(jī)即可。圖 1 給出了?jconsole?JMX 客戶機(jī)(JDK 的一部分) ,它顯示了其中一個(gè)內(nèi)存管理視圖——一段時(shí)間內(nèi)的堆使用情況。Perform GC 按鈕則證明了 JMX 可以提供 除了查看操作統(tǒng)計(jì)值之外的初始化操作的功能。
圖 1. 用 jconsole 查看堆使用情況
傳輸和安全性
JMX 指定了在?MBeanServer?和 JMX 客戶之間通信所使用的協(xié)議,協(xié)議可以在各種傳輸機(jī)制上運(yùn)行。可以使用針對(duì)本地連接的內(nèi)置傳輸,及通過 RMI、socket 或 SSL 的遠(yuǎn)程傳輸(可以通過 JMX Connector API 創(chuàng)建新的傳輸)。認(rèn)證是由傳輸執(zhí)行的;本地傳輸允許用相同的用戶 ID 連接到運(yùn)行在本地系統(tǒng)上的 JVM;遠(yuǎn)程傳輸可以用口令或證書進(jìn)行認(rèn)證。本地傳輸在 Java 6 下默認(rèn)就是啟用的。要在 Java 5.0 下啟用它,需要在 JVM 啟動(dòng)時(shí)定義系統(tǒng)屬性?com.sun.management.jmxremote。“Monitoring and Management using JMX” 這份文檔(請(qǐng)參閱參考資料)描述了啟用和配置傳輸?shù)呐渲貌襟E。
檢測 Web 服務(wù)器
檢測應(yīng)用程序來使用 JMX 很容易。像其他許多遠(yuǎn)程調(diào)用框架(RMI、EJB 和 JAX-RPC)一樣,JMX 也是基于接口的。要?jiǎng)?chuàng)建管理服務(wù),需要?jiǎng)?chuàng)建指定管理方法的 MBean 接口。然后可以創(chuàng)建一個(gè) MBean 來實(shí)現(xiàn)此接口、實(shí)例化它及把它注冊(cè)到?MBeanServer。
清單 1 顯示了網(wǎng)絡(luò)服務(wù)(例如 Web 服務(wù)器)的 MBean 接口。它提供了檢索配置信息(例如端口號(hào))和操作性信息(例如服務(wù)是否啟動(dòng))的 getter。它還包含查看和修改可配置參數(shù)(例如當(dāng)前日志級(jí)別)的 getter 和 setter,還有調(diào)用管理操作(例如?start()?和?stop())的方法。
清單 1. 某個(gè) Web 服務(wù)器的 MBean 接口
| 1 2 3 4 5 6 7 8 9 10 | public interface WebServerMBean { ????public int getPort(); ? ????public String getLogLevel(); ????public void setLogLevel(String level); ? ????public boolean isStarted(); ????public void stop(); ????public void start(); } |
實(shí)現(xiàn) MBean 類通常非常直接明了,因?yàn)?MBean 接口要反映現(xiàn)有實(shí)體或服務(wù)的屬性和管理操作。例如,MBean 中的?getLogLevel()?和?setLogLevel()?方法會(huì)直接轉(zhuǎn)給被 Web 服務(wù)器使用的?Logger?上的?getLevel()?和?setLevel()?方法。JMX 做了一些命名限制。例如,MBean 接口名稱必須以?MBean?結(jié)尾,FooMBean?接口的 MBean 類必須叫作?Foo。(可以用更高級(jí)的 JMX 特性——?jiǎng)討B(tài) MBean 來去除這個(gè)限制。)把 MBean 注冊(cè)到默認(rèn)的?MBeanServer?也很容易,如清單 2 所示:
清單 2. 用內(nèi)置的 JMX 實(shí)現(xiàn)注冊(cè) MBean
| 1 2 3 4 5 6 7 | public class WebServer implements WebServerMBean { ... } ? ... ? WebServer ws = new WebServer(...); MBeanServer server = ManagementFactory.getPlatformMBeanServer(); server.registerMBean(ws, new ObjectName("myapp:type=webserver,name=Port 8080")); |
傳遞給?registerMBean()?的?ObjectName?標(biāo)識(shí)了受管實(shí)體。因?yàn)轭A(yù)見到指定應(yīng)用程序可能包含許多受管實(shí)體,所以名稱包含域(清單 2 中的 “myapp”)和許多標(biāo)識(shí)域中的受管資源的鍵-值對(duì)。“name” 和 “type” 這兩個(gè)鍵是常用的,在使用的時(shí)候,name 應(yīng)當(dāng)在域中所有的同類 MBean 中能夠唯一地標(biāo)識(shí)受管實(shí)體。也可以指定其他鍵-值對(duì),而且 JMX API 還包含進(jìn)行對(duì)象名稱通配匹配的工具。
創(chuàng)建并注冊(cè)了 MBean 之后,立即就可以把?jconsole?指向應(yīng)用程序(在命令行輸入?jconsole)并在 “MBeans” 視圖中查看它的管理屬性和操作。圖 2 顯示了?jconsole?中針對(duì)新 MBean 的 Attributes 標(biāo)簽,圖 3 顯示了 Operations 標(biāo)簽。使用反射,JMX 可以指出哪個(gè)屬性是只讀的(Started、Port),哪個(gè)屬性是可讀寫的(LogLevel),而且?jconsole?允許修改讀寫屬性。如果讀寫屬性的 setter 拋出異常(例如?IllegalArgumentException),JMX 就把異常報(bào)告給客戶機(jī)。
圖 2. jconsole 中 MBean 的 Attributes 標(biāo)簽
圖 3. jconsole 中 MBean 的 Operations 標(biāo)簽
數(shù)據(jù)類型
MBean 中的訪問器和操作能夠用任何其簽名形式的原語類型,以及?String、Date?和其他標(biāo)準(zhǔn)庫類。也可以使用這些允許的類型的數(shù)組和集合。MBean 方法也可以使用其他可以序列化的數(shù)據(jù)類型,但是這樣做會(huì)造成互操作性問題,因?yàn)轭愇募脖仨殞?duì) JMX 客戶機(jī)可用。(如果使用 RMI 傳輸,可以使用 RMI 的自動(dòng)類下載特性完成這項(xiàng)任務(wù)。)如果想在管理接口中使用結(jié)構(gòu)化數(shù)據(jù)類型,還想避免與類可用性相關(guān)的互操作性問題,可以使用 JMX 的開放 MBean 特性來表達(dá)復(fù)合或表格數(shù)據(jù)。
檢測服務(wù)器應(yīng)用程序
在創(chuàng)建管理接口時(shí),某些參數(shù)和操作的特點(diǎn)很自然地就表明這些參數(shù)和數(shù)據(jù)應(yīng)當(dāng)被包含在內(nèi),例如配置參數(shù)、操作統(tǒng)計(jì)值、調(diào)試操作(例如修改日志級(jí)別或把應(yīng)用程序狀態(tài)導(dǎo)出到文件)、生命周期操作(啟動(dòng)、停止)。檢測一個(gè)應(yīng)用程序,讓它支持對(duì)這些屬性和操作的訪問,通常相當(dāng)容易。但是,要從 JMX 獲得最大價(jià)值,就要在設(shè)計(jì)時(shí)考慮什么數(shù)據(jù)在運(yùn)行時(shí)對(duì)用戶和操作員有用。
如果用 JMX 了解服務(wù)器應(yīng)用程序的工作情況,需要一種標(biāo)識(shí)和跟蹤工作單元的機(jī)制。如果使用標(biāo)準(zhǔn)的?Runnable?和?Callable?接口描述任務(wù),通過讓任務(wù)類自描述(例如實(shí)現(xiàn)toString()?方法),可以在任務(wù)生命周期內(nèi)跟蹤它們,并提供 MBean 方法來返回等候中、處理中和完成的任務(wù)列表。
清單 3 中的?TrackingThreadPool?演示的是?ThreadPoolExecutor?的一個(gè)子類,它及時(shí)給出正在處理中的是哪些任務(wù),以及已經(jīng)完成的任務(wù)的時(shí)間統(tǒng)計(jì)值。它通過覆蓋?beforeExecute()?和?afterExecute()掛鉤,并提供能檢索所搜集數(shù)據(jù)的 getter,實(shí)現(xiàn)這些任務(wù)。
清單 3. 搜集處理中的任務(wù)和平均的任務(wù)時(shí)間統(tǒng)計(jì)值的線程池類
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class TrackingThreadPool extends ThreadPoolExecutor { ????private final Map<Runnable, Boolean> inProgress ????????= new ConcurrentHashMap<Runnable,Boolean>(); ????private final ThreadLocal<Long> startTime = new ThreadLocal<Long>(); ????private long totalTime; ????private int totalTasks; ? ????public TrackingThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, ???????TimeUnit unit, BlockingQueue<Runnable> workQueue) { ????????super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); ????} ? ????protected void beforeExecute(Thread t, Runnable r) { ????????super.beforeExecute(t, r); ????????inProgress.put(r, Boolean.TRUE); ????????startTime.set(new Long(System.currentTimeMillis())); ????} ? ????protected void afterExecute(Runnable r, Throwable t) { ????????long time = System.currentTimeMillis() - startTime.get().longValue(); ????????synchronized (this) { ????????????totalTime += time; ????????????++totalTasks; ????????} ????????inProgress.remove(r); ????????super.afterExecute(r, t); ????} ? ????public Set<Runnable> getInProgressTasks() { ????????return Collections.unmodifiableSet(inProgress.keySet()); ????} ? ????public synchronized int getTotalTasks() { ????????return totalTasks; ????} ? ????public synchronized double getAverageTaskTime() { ????????return (totalTasks == 0) ? 0 : totalTime / totalTasks; ????} } |
清單 4 中的?ThreadPoolStatusMBean?顯示了?TrackingThreadPool?的 MBean 接口,它提供了活動(dòng)任務(wù)、活動(dòng)線程、完成任務(wù)、等候任務(wù)的計(jì)數(shù),還提供了當(dāng)前等候執(zhí)行和正在執(zhí)行的任務(wù)的列表。在管理接口中包含等候和執(zhí)行任務(wù)的列表,讓您既可以看到應(yīng)用程序的工作難度,又可以看到它目前的工作內(nèi)容。這個(gè)特性不僅讓您可以洞察應(yīng)用程序的行為,還能洞察它正在操作的數(shù)據(jù)集的性質(zhì)。
清單 4. TrackingThreadPool 的 MBean 接口
| 1 2 3 4 5 6 7 8 9 | public interface ThreadPoolStatusMBean { ????public int getActiveThreads(); ????public int getActiveTasks(); ????public int getTotalTasks(); ????public int getQueuedTasks(); ????public double getAverageTaskTime(); ????public String[] getActiveTaskNames(); ????public String[] getQueuedTaskNames(); } |
如果任務(wù)的重量級(jí)足夠,那么甚至可以再進(jìn)一步,在每個(gè)任務(wù)提交時(shí)都為它注冊(cè)一個(gè) MBean (然后在任務(wù)完成時(shí)再取消注冊(cè))。然后可以用管理接口查詢每個(gè)任務(wù)的當(dāng)前狀態(tài)、運(yùn)行了多長時(shí)間,或者請(qǐng)求取消任務(wù)。
清單 5 中的?ThreadPoolStatus?實(shí)現(xiàn)了?ThreadPoolStatusMBean?接口,它提供了每個(gè)訪問器的明顯實(shí)現(xiàn)。與 MBean 實(shí)現(xiàn)類中的典型情況一樣,每個(gè)操作實(shí)現(xiàn)起來都很細(xì)碎,所以把實(shí)現(xiàn)委托給了底層受管對(duì)象。在這個(gè)示例中,JMX 代碼完全獨(dú)立于受管實(shí)體的代碼。TrackingThreadPool?對(duì)于 JMX 一無所知;通過為相關(guān)的屬性提供管理方法和訪問器,它提供了自己的編程管理接口。 還可以選擇在實(shí)現(xiàn)類中直接實(shí)現(xiàn)管理功能(讓?TrackingThreadPool?實(shí)現(xiàn)?TrackingThreadPoolMBean?接口),或者單獨(dú)實(shí)現(xiàn)(如清單 4 和 5 所示)。
清單 5. TrackingThreadpool 的 MBean 實(shí)現(xiàn)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public class ThreadPoolStatus implements ThreadPoolStatusMBean { ????private final TrackingThreadPool pool; ? ????public ThreadPoolStatus(TrackingThreadPool pool) { ????????this.pool = pool; ????} ? ????public int getActiveThreads() { ????????return pool.getPoolSize(); ????} ? ????public int getActiveTasks() { ????????return pool.getActiveCount(); ????} ? ????public int getTotalTasks() { ????????return pool.getTotalTasks(); ????} ? ????public int getQueuedTasks() { ????????return pool.getQueue().size(); ????} ? ????public double getAverageTaskTime() { ????????return pool.getAverageTaskTime(); ????} ? ????public String[] getActiveTaskNames() { ????????return toStringArray(pool.getInProgressTasks()); ????} ? ????public String[] getQueuedTaskNames() { ????????return toStringArray(pool.getQueue()); ????} ? ????private String[] toStringArray(Collection<Runnable> collection) { ????????ArrayList<String> list = new ArrayList<String>(); ????????for (Runnable r : collection) ????????????list.add(r.toString()); ????????return list.toArray(new String[0]); ????} } |
為了演示這些類如何提供對(duì)應(yīng)用程序操作的內(nèi)容的了解,請(qǐng)考慮這樣一個(gè) Web 搜尋應(yīng)用程序,它把工作分成兩類任務(wù):獲取遠(yuǎn)程頁面,對(duì)頁面進(jìn)行索引。每個(gè)任務(wù)分別用清單 6 所示的?FetchTask?或?IndexTask?描述。可以創(chuàng)建?ThreadPoolStatus?MBean,提供處理這些任務(wù)所使用的線程池的管理接口,并把它用 JMX 注冊(cè)。
清單 6. Web 搜尋應(yīng)用程序中使用的 FetchTask 類
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | public class FetchTask implements Runnable { ????private final String name; ? ????public FetchTask(String name) { ????????this.name = name; ????} ? ????public String toString() { ????????return "FetchTask: " + name; ????} ? ????public void run() {? /* Fetch remote resource */? } } |
當(dāng)此程序處理每個(gè)頁面時(shí),可能還會(huì)對(duì)新任務(wù)進(jìn)行排隊(duì)以獲取這個(gè)頁面上鏈接的頁面,所以在指定時(shí)間內(nèi),可能會(huì)既有獲取任務(wù)又有尚未完成的索引任務(wù)。能夠正確地判斷正在處理哪個(gè)頁面,或者正在等候處理哪個(gè)頁面,不僅讓您可以理解應(yīng)用程序的性能特征,還可以理解應(yīng)用程序所操作的數(shù)據(jù)的特征。
圖 4 顯示了正在處理 whitehouse.gov 站點(diǎn)的 Web 搜尋程序的快照。從圖中可以看到已經(jīng)獲取并索引了主頁,程序現(xiàn)在的工作是獲取和索引直接從該主頁鏈接出的頁面。單擊?Refresh?按鈕,可以對(duì)應(yīng)用程序的工作流程進(jìn)行取樣,它可以提供許多關(guān)于應(yīng)用程序工作情況的信息,卻不需引入大量日志或者在調(diào)試器中運(yùn)行應(yīng)用程序。
圖 4. Web 搜尋應(yīng)用程序中的活動(dòng)任務(wù)和排隊(duì)任務(wù)
結(jié)束語
結(jié)合平臺(tái)內(nèi)的 JMX 支持和?jconsole?JMX 客戶機(jī)可以提供一種向應(yīng)用程序添加管理和監(jiān)視功能的輕松方式。即使是沒有具體管理需求的應(yīng)用程序,為它們構(gòu)建這些功能也會(huì)讓您對(duì)程序的運(yùn)行及其所處理的數(shù)據(jù)的性質(zhì)獲得深入了解,而且不需太多的工作和努力。如果應(yīng)用程序?qū)С龉芾斫涌?#xff0c;此接口讓您可以查看它操作的內(nèi)容,那么您就會(huì)更加了解它的運(yùn)行狀態(tài)——對(duì)它是否按預(yù)期的方式工作也會(huì)更有信心——而不必求助于額外的工具(例如添加日志代碼或使用調(diào)試器或分析器)。
相關(guān)主題
- 您可以參閱本文在 developerWorks 全球站點(diǎn)上的?英文原文?。
- “Monitoring and Managing Using JMX”(Sun Microsystems Inc.,2004):詳細(xì)介紹了內(nèi)置的 JMX 代理的配置和使用。
- JMX Best Practices (Sun Developer Network, 1994-2006):描述了命名管理對(duì)象、選擇 JMX 特性和為受管屬性選擇數(shù)據(jù)類型的最佳實(shí)踐。
- 使用 JMX 管理 Apache Geronimo (developerWorks, J. Jeffrey Hanson, 2006 年 8 月):學(xué)習(xí) Geronimo 應(yīng)用程序服務(wù)器如何利用 JMX 來方便應(yīng)用程序的管理。
- JMX:下載 JMX 規(guī)范和文檔。
- MBeanInspector for WebSphere Application Server:基于 JMX 的管理工具,用于 WebSphere 5。
- Java 技術(shù)專區(qū):這里有數(shù)百篇有關(guān) Java 編程各方面的文章。
from:https://www.ibm.com/developerworks/cn/java/j-jtp09196/index.html?
總結(jié)
以上是生活随笔為你收集整理的用 JMX 检测应用程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JMX 与系统管理
- 下一篇: Git 分支 - rebase 变基