Java學(xué)習(xí)

          java,spring,structs,hibernate,jsf,ireport,jfreechart,jasperreport,tomcat,jboss -----本博客已經(jīng)搬家了,新的地址是 http://www.javaly.cn 如果有對(duì)文章有任何疑問或者有任何不懂的地方,歡迎到www.javaly.cn (Java樂園)指出,我會(huì)盡力幫助解決。一起進(jìn)步

           

          Java 理論與實(shí)踐: 用JMX檢測(cè)應(yīng)用程序


          【IT168 技術(shù)文檔】

              調(diào)試器和分析器可以提供對(duì)應(yīng)用程序的行為的深入觀察,但在出現(xiàn)嚴(yán)重問題之前,這些工具通常用不上。將監(jiān)視掛鉤(hook)構(gòu)建到應(yīng)用程序內(nèi),會(huì)使理解程序 的執(zhí)行變得更容易而且不會(huì)破壞調(diào)試器。既然 Java 管理擴(kuò)展(JMX)已經(jīng)構(gòu)建進(jìn)了 Java SE 平臺(tái),而且 jconsole 查看器提供了統(tǒng)一的監(jiān)視 GUI,那么用 JMX 為應(yīng)用程序提供一個(gè)窗口,要比以前更加容易而且更為有效。

          有多少次您曾經(jīng)注視著運(yùn)行中的應(yīng)用程序,問自己:“它到底在做什么?為什么用了這么長(zhǎ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 頁(yè)面上顯示統(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 中,類庫(kù)和和 JVM 提供了一種全面的管理和監(jiān)視基礎(chǔ)設(shè)施——JMX。JMX 是一種用來提供可以遠(yuǎn)程訪問的管理接口的標(biāo)準(zhǔn)措施,也是一種向應(yīng)用程序添加靈活且強(qiáng)大的管理接口的簡(jiǎn)易方式。被稱作受管 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” 這份文檔描述了啟用和配置傳輸?shù)呐渲貌襟E。

          檢測(cè) Web 服務(wù)器

          檢測(cè)應(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 接口

          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é)尾,F(xiàn)ooMBean 接口的 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

          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)庫(kù)類。也可以使用這些允許的類型的數(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ù)。

          檢測(cè)服務(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)、停止)。檢測(cè)一個(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ì)值的線程池類

          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 接口

          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)行了多長(zhǎng)時(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)

          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)程頁(yè)面,對(duì)頁(yè)面進(jìn)行索引。每個(gè)任務(wù)分別用清單 6 所示的 FetchTask 或 IndexTask 描述。可以創(chuàng)建 ThreadPoolStatus MBean,提供處理這些任務(wù)所使用的線程池的管理接口,并把它用 JMX 注冊(cè)。

          清單 6. Web 搜尋應(yīng)用程序中使用的 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 */ }
          }

            當(dāng)此程序處理每個(gè)頁(yè)面時(shí),可能還會(huì)對(duì)新任務(wù)進(jìn)行排隊(duì)以獲取這個(gè)頁(yè)面上鏈接的頁(yè)面,所以在指定時(shí)間內(nèi),可能會(huì)既 有獲取任務(wù)又有尚未完成的索引任務(wù)。能夠正確地判斷正在處理哪個(gè)頁(yè)面,或者正在等候處理哪個(gè)頁(yè)面,不僅讓您可以理解應(yīng)用程序的性能特征,還可以理解應(yīng)用程 序所操作的數(shù)據(jù)的特征。

          圖 4 顯示了正在處理 whitehouse.gov 站點(diǎn)的 Web 搜尋程序的快照。從圖中可以看到已經(jīng)獲取并索引了主頁(yè),程序現(xiàn)在的工作是獲取和索引直接從該主頁(yè)鏈接出的頁(yè)面。單擊 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ū)С龉芾斫涌冢私涌谧屇梢圆榭此僮鞯膬?nèi)容,那么您就會(huì)更加了解它的運(yùn)行狀態(tài)——對(duì)它 是否按預(yù)期的方式工作也會(huì)更有信心——而不必求助于額外的工具(例如添加日志代碼或使用調(diào)試器或分析器)。

          posted on 2008-07-22 16:37 找個(gè)美女做老婆 閱讀(426) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計(jì)

          公告

          本blog已經(jīng)搬到新家了, 新家:www.javaly.cn
           http://www.javaly.cn

          常用鏈接

          留言簿(6)

          隨筆檔案

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 绍兴县| 镇赉县| 滨州市| 福海县| 清新县| 英山县| 九台市| 黔南| 张掖市| 枣庄市| 龙游县| 孟津县| 通榆县| 蚌埠市| 通州区| 平塘县| 乐业县| 无棣县| 喀喇沁旗| 余姚市| 武功县| 鹤壁市| 互助| 大竹县| 乌拉特后旗| 渭南市| 张北县| 海阳市| 三原县| 双城市| 文登市| 正定县| 罗田县| 安多县| 读书| 阿拉善右旗| 信阳市| 梓潼县| 昌平区| 司法| 乃东县|