程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java理論與實踐:用JMX檢測應用程序

Java理論與實踐:用JMX檢測應用程序

編輯:關於JAVA

有多少次您曾經注視著運行中的應用程序,問自己:“它到底在做什麼?為 什麼用了這麼長時間呢?” 在這些時刻,您可能會想如果自己在應用程序中構 建了更多的監視功能就好了。例如,在服務器應用程序中,能夠查看排隊等候處 理的任務的數量和類型、當前正在處理的任務、過去一分鐘或一小時內的吞吐量 統計、平均任務處理時間等。這些統計值容易搜集,但是在需要數據的時候,如 果沒有非侵入性的數據檢索機制,那麼這些值就不太有用。

可以用許多方式導出操作性數據——可以把周期性統計快照寫入日志文件、 創建 Swing GUI、使用內嵌的 HTTP 服務器在 Web 頁面上顯示統計值或者發布 可以用來查詢應用程序的 Web 服務。但是在缺少監視和數據發布基礎設施的情 況下,多數應用程序開發人員都做不到這些,因此造成對應用程序工作情況的了 解要比預期的少很多。

JMX

在 Java 5.0 中,類庫和和 JVM 提供了一種全面的管理和監視基礎設施—— JMX。JMX 是一種用來提供可以遠程訪問的管理接口的標准措施,也是一種向應 用程序添加靈活且強大的管理接口的簡易方式。被稱作受管 bean(MBean)的 JMX 組件,是提供與實體的管理有關的訪問器和業務方法的 JavaBean。每個受 管的實體(可能是整個應用程序或應用程序中的服務)實例化一個 MBean 並用 可讀懂的名稱注冊它。支持 JMX 的應用程序依賴於 MBeanServer,它充當 MBean 的容器,提供遠程訪問、命名空間管理和安全服務。在客戶端,jconsole 工具可以充當統一的 JMX 客戶機。結合兩者,對 JMX 的平台支持極大地降低了 使應用程序支持外部管理接口所需的工作和努力。

除了提供 MBeanServer 實現,Java SE 5.0 還提供 JVM 以更方便地了解內 存管理、類裝入、活動線程、日志和平台配置的狀態。多數平台服務的監視和管 理在默認情況下都是開啟的(性能影響最小),所以只需要連接應用程序與 JMX 客戶機即可。圖 1 給出了 jconsole JMX 客戶機(JDK 的一部分) ,它顯示了 其中一個內存管理視圖——一段時間內的堆使用情況。Perform GC 按鈕則證明 了 JMX 可以提供 除了查看操作統計值之外的初始化操作的功能。

圖 1. 用 jconsole 查看堆使用情況

傳輸和安全性

JMX 指定了在 MBeanServer 和 JMX 客戶之間通信所使用的協議,協議可以 在各種傳輸機制上運行。可以使用針對本地連接的內置傳輸,及通過 RMI、 socket 或 SSL 的遠程傳輸(可以通過 JMX Connector API 創建新的傳輸)。 認證是由傳輸執行的;本地傳輸允許用相同的用戶 ID 連接到運行在本地系統上 的 JVM;遠程傳輸可以用口令或證書進行認證。本地傳輸在 Java 6 下默認就是 啟用的。要在 Java 5.0 下啟用它,需要在 JVM 啟動時定義系統屬性 com.sun.management.jmxremote。“Monitoring and Management using JMX” 這份文檔(請參閱參考資料)描述了啟用和配置傳輸的配置步驟。

檢測 Web 服務器

檢測應用程序來使用 JMX 很容易。像其他許多遠程調用框架(RMI、EJB 和 JAX-RPC)一樣,JMX 也是基於接口的。要創建管理服務,需要創建指定管理方 法的 MBean 接口。然後可以創建一個 MBean 來實現此接口、實例化它及把它注 冊到 MBeanServer。

清單 1 顯示了網絡服務(例如 Web 服務器)的 MBean 接口。它提供了檢索 配置信息(例如端口號)和操作性信息(例如服務是否啟動)的 getter。它還 包含查看和修改可配置參數(例如當前日志級別)的 getter 和 setter,還有 調用管理操作(例如 start() 和 stop())的方法。

清單 1. 某個 Web 服務器的 MBean 接口

public interface WebServerMBean {
   public int getPort();
   public String getLogLevel();
   public void setLogLevel(String level);
   public boolean isStarted();
   public void stop();
   public void start();
}

實現 MBean 類通常非常直接明了,因為 MBean 接口要反映現有實體或服務 的屬性和管理操作。例如,MBean 中的 getLogLevel() 和 setLogLevel() 方法 會直接轉給被 Web 服務器使用的 Logger 上的 getLevel() 和 setLevel() 方 法。JMX 做了一些命名限制。例如,MBean 接口名稱必須以 MBean 結尾, FooMBean 接口的 MBean 類必須叫作 Foo。(可以用更高級的 JMX 特性——動 態 MBean 來去除這個限制。)把 MBean 注冊到默認的 MBeanServer 也很容易 ,如清單 2 所示:

清單 2. 用內置的 JMX 實現注冊 MBean

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 標識了受管實體。因為預見到指定 應用程序可能包含許多受管實體,所以名稱包含域(清單 2 中的 “myapp”) 和許多標識域中的受管資源的鍵-值對。“name” 和 “type” 這兩個鍵是常用 的,在使用的時候,name 應當在域中所有的同類 MBean 中能夠唯一地標識受管 實體。也可以指定其他鍵-值對,而且 JMX API 還包含進行對象名稱通配匹配的 工具。

創建並注冊了 MBean 之後,立即就可以把 jconsole 指向應用程序(在命令 行輸入 jconsole)並在 “MBeans” 視圖中查看它的管理屬性和操作。圖 2 顯 示了 jconsole 中針對新 MBean 的 Attributes 標簽,圖 3 顯示了 Operations 標簽。使用反射,JMX 可以指出哪個屬性是只讀的(Started、Port ),哪個屬性是可讀寫的(LogLevel),而且 jconsole 允許修改讀寫屬性。如 果讀寫屬性的 setter 拋出異常(例如 IllegalArgumentException),JMX 就 把異常報告給客戶機。

圖 2. jconsole 中 MBean 的 Attributes 標簽

圖 3. jconsole 中 MBean 的 Operations 標簽

數據類型

MBean 中的訪問器和操作能夠用任何其簽名形式的原語類型,以及 String、 Date 和其他標准庫類。也可以使用這些允許的類型的數組和集合。MBean 方法 也可以使用其他可以序列化的數據類型,但是這樣做會造成互操作性問題,因為 類文件也必須對 JMX 客戶機可用。(如果使用 RMI 傳輸,可以使用 RMI 的自 動類下載特性完成這項任務。)如果想在管理接口中使用結構化數據類型,還想 避免與類可用性相關的互操作性問題,可以使用 JMX 的開放 MBean 特性來表達 復合或表格數據。

檢測服務器應用程序

在創建管理接口時,某些參數和操作的特點很自然地就表明這些參數和數據 應當被包含在內,例如配置參數、操作統計值、調試操作(例如修改日志級別或 把應用程序狀態導出到文件)、生命周期操作(啟動、停止)。檢測一個應用程 序,讓它支持對這些屬性和操作的訪問,通常相當容易。但是,要從 JMX 獲得 最大價值,就要在設計時考慮什麼數據在運行時對用戶和操作員有用。

如果用 JMX 了解服務器應用程序的工作情況,需要一種標識和跟蹤工作單元 的機制。如果使用標准的 Runnable 和 Callable 接口描述任務,通過讓任務類 自描述(例如實現toString() 方法),可以在任務生命周期內跟蹤它們,並提 供 MBean 方法來返回等候中、處理中和完成的任務列表。

清單 3 中的 TrackingThreadPool 演示的是 ThreadPoolExecutor 的一個子 類,它及時給出正在處理中的是哪些任務,以及已經完成的任務的時間統計值。 它通過覆蓋 beforeExecute() 和 afterExecute() 掛鉤,並提供能檢索所搜集 數據的 getter,實現這些任務。

清單 3. 搜集處理中的任務和平均的任務時間統計值的線程池類

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 接口,它提供了活動任務、活動線程、完成任務、等候任務的計數,還提 供了當前等候執行和正在執行的任務的列表。在管理接口中包含等候和執行任務 的列表,讓您既可以看到應用程序的工作難度,又可以看到它目前的工作內容。 這個特性不僅讓您可以洞察應用程序的行為,還能洞察它正在操作的數據集的性 質。

清單 4. TrackingThreadPool 的 MBean 接口

public interface ThreadPoolStatusMBean {
   public int getActiveThreads();
   public int getActiveTasks();
   public int getTotalTasks();
   public int getQueuedTasks();
   public double getAverageTaskTime();
   public String[] getActiveTaskNames();
   public String[] getQueuedTaskNames();
}

如果任務的重量級足夠,那麼甚至可以再進一步,在每個任務提交時都為它 注冊一個 MBean (然後在任務完成時再取消注冊)。然後可以用管理接口查詢 每個任務的當前狀態、運行了多長時間,或者請求取消任務。

清單 5 中的 ThreadPoolStatus 實現了 ThreadPoolStatusMBean 接口,它 提供了每個訪問器的明顯實現。與 MBean 實現類中的典型情況一樣,每個操作 實現起來都很細碎,所以把實現委托給了底層受管對象。在這個示例中,JMX 代 碼完全獨立於受管實體的代碼。TrackingThreadPool 對於 JMX 一無所知;通過 為相關的屬性提供管理方法和訪問器,它提供了自己的編程管理接口。 還可以 選擇在實現類中直接實現管理功能(讓 TrackingThreadPool 實現 TrackingThreadPoolMBean 接口),或者單獨實現(如清單 4 和 5 所示)。

清單 5. TrackingThreadpool 的 MBean 實現

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]);
   }
}

為了演示這些類如何提供對應用程序操作的內容的了解,請考慮這樣一個 Web 搜尋應用程序,它把工作分成兩類任務:獲取遠程頁面,對頁面進行索引。 每個任務分別用清單 6 所示的 FetchTask 或 IndexTask 描述。可以創建 ThreadPoolStatus MBean,提供處理這些任務所使用的線程池的管理接口,並把 它用 JMX 注冊。

清單 6. Web 搜尋應用程序中使用的 FetchTask 類

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 */ }
}

當此程序處理每個頁面時,可能還會對新任務進行排隊以獲取這個頁面上鏈 接的頁面,所以在指定時間內,可能會既有獲取任務又有尚未完成的索引任務。 能夠正確地判斷正在處理哪個頁面,或者正在等候處理哪個頁面,不僅讓您可以 理解應用程序的性能特征,還可以理解應用程序所操作的數據的特征。

圖 4 顯示了正在處理 whitehouse.gov 站點的 Web 搜尋程序的快照。從圖 中可以看到已經獲取並索引了主頁,程序現在的工作是獲取和索引直接從該主頁 鏈接出的頁面。單擊 Refresh 按鈕,可以對應用程序的工作流程進行取樣,它 可以提供許多關於應用程序工作情況的信息,卻不需引入大量日志或者在調試器 中運行應用程序。

圖 4. Web 搜尋應用程序中的活動任務和排隊任務

結束語

結合平台內的 JMX 支持和 jconsole JMX 客戶機可以提供一種向應用程序添 加管理和監視功能的輕松方式。即使是沒有具體管理需求的應用程序,為它們構 建這些功能也會讓您對程序的運行及其所處理的數據的性質獲得深入了解,而且 不需太多的工作和努力。如果應用程序導出管理接口,此接口讓您可以查看它操 作的內容,那麼您就會更加了解它的運行狀態——對它是否按預期的方式工作也 會更有信心——而不必求助於額外的工具(例如添加日志代碼或使用調試器或分 析器)。

參考資料

學習

您可以參閱本文在 developerWorks 全球站點上的 英文原文 。

“Monitoring and Managing Using JMX”(Sun Microsystems Inc.,2004 ):詳細介紹了內置的 JMX 代理的配置和使用。

JMX Best Practices (Sun Developer Network, 1994-2006):描述了命名管 理對象、選擇 JMX 特性和為受管屬性選擇數據類型的最佳實踐。

使用 JMX 管理 Apache Geronimo (developerWorks, J. Jeffrey Hanson, 2006 年 8 月):學習 Geronimo 應用程序服務器如何利用 JMX 來方便應用程序 的管理。

Java 技術專區:這裡有數百篇有關 Java 編程各方面的文章。

關於作者

Brian Goetz 作為專業的軟件開發人員已經有 20 年了。他是 Sun Microsystems 的高級工程師,並且效力於多個 JCP 專家組。Brian 的新書 Java Concurrency In Practice 在 2006 年 5 月由 Addison-Wesley 出版。請 參閱 Brian 在流行的業界出版物上 已發表和即將發表的文章。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved