为Web应用建立基于JMX的管理系统
Web應用系統總算開發了,接下來該如何讓客戶(Web應用系統的管理員)輕松管理我的一堆配置文件,或者如何實現動態修改系統運行屬性,同時又讓客戶不需要過多的了解配置文件的內容就能夠實現這些管理呢?這是許多剛剛結束Web應用系統前期開發的系統分析人員需要面臨的問題。又或者說我想對早已完成 Web應用系統進行有效的資源管理,希望再添加管理功能的同時,對原有的代碼不需要做過多的修改,換句話說就是管理系統與被管理的應用系統做到很好的隔離。JMX的管理框架(圖1)為你很好的解決了這些問題。
JMX(Java Management Extensions)是來管理網絡,設備,應用程序等資源,它描述了一個可擴展的管理體系結構,并且提供了JMX API和一些欲定義的java管理服務。在撰寫本文時,JMX規范最新版本為v1.2(http: //jcp.org/aboutJava/communityprocess/final/jsr003/index3.html),JMX參考實現的最新版本為v1.2.1(http://java.sun.com/products/JavaManagement/)。JMX推出后,一些大型的項目就立即采用了基于JMX的實現框架,例如Jakarta tomcat和JBoss,這從分說明JMX的可行性和良好的特性。
對于 Web應用的管理往往是比較麻煩的,例如客戶手動的修改配置文件,開啟數據庫監控程序等等,如果要動態修改數據庫訪問方案或者監控用戶數,動態修改日志級別會更加麻煩,并且可能把系統的結構弄得凌亂,造成結構不良的惡果,更別說可擴展性了。JMX的分層結構以及高度的組件化,通過將各種資源封裝成 MBean的方式,讓我們可以很低成本的實現對現有Web應用的擴展性很強的管理方案。
本文以Tomcat作為Web服務器為例,詳細的介紹如何使用JMX建立對Web應用的管理。對于JMX的概念性的東西、體系結構以及使用規范已經有不少的相關文檔,為了能夠更好的理解本文,在閱讀本文時請先參閱這些文檔,本文的筆墨將著重在應用和實現上。下圖(圖2)為JMX的基本框架圖(見JMX規范),,目的是給大家理解本文提供方便。
一、創建Web應用的管理系統
對Web應用構建一個基于JMX的管理系統,我們需要做的事情有哪些呢?
針對每一個需要管理的資源創建一個MBean的實例,這是JMX框架所要求的,有兩種類型的實例可供選擇,一種是直接管理資源的MBean,一種通過調用資源實例進行管理的MBean。
編寫一個MBean描述文件,并描述每一個MBean,選擇基于XML的MBean描述文件是一個不錯的決定。
通過讀MBean描述文件,生成MBeanInfo,從而生成一個個MBean。
將需要進行管理的MBean注冊到MBean Server當中。
編寫客戶端代碼,選擇Web的方式進行客戶端的編碼比較Web應用的風格,也比較容易實現。
那么一個基于JMX的Web應用的管理框架已經成形,圖3是它的基本結構圖,虛線部分為基于JMX的管理系統。接下來我們按照步驟實現整個管理系統。
二、獲得MBeanServer的實例
有兩種方案獲得MBeanServer的實例。
1、通過獲得Web服務器的MBeanServer的實例,這樣做的好處是通過該MBeanServer對本身,甚至可以實現對Web服務器的自身的一些管理。Tomcat的管理框架也是建立JMX的基礎上,它使用的JMX的實現是MX4J,這是一個非常優秀的JMX開源項目,在 tomcat4.1.27中,MBeanServer的實例存放與屬性名為"org.apache.catalina.MBeanServer"的 application變量(Web應用中變量的幾種范圍:page,request,session,application)中,因此servlet 中獲得MBeanServer實例的辦法:
server = (MBeanServer)
getServletContext().getAttribute("org.apache.catalina.MBeanServer");
如果通過這種方式,你獲得的server為null,這說明你必須還要完成下面的工作,使你能夠有權限獲得系統的 MBeanServer,tomcat才會將MBeanServer的實例存放在web應用程序下屬性名 "org.apache.catalina.MBeanServer"的系統變量中。
找到tomcat下conf目錄,修改server.xml文件。修改Web應用的context元素,添加上privileged="true"這一項屬性即可,例如:
<Context path="/myapp" docBase="c:/web/" debug="9"
privileged="true" reloadable="true" crossContext="true"/>
2、通過JMX API中MBeanServerFactory類的createMbeanServer()的方法創建MBeanServer的實例,這樣做得好處的使 JMX的實現與Web服務器無關,使代碼的移植性更強。在創建完MBeanServer以后,為了讓能夠在管理系統中很方便的獲得該 MBeanServer的引用,可將其置入application變量中(推薦),或者使用singleton設計模式的方法創建和獲得。
使用JMX API創建MBean Server的代碼如下:
MBeanServer server = MBeanServerFactory.createMBeanServer();
究竟采取何種方案獲得MBeanServer并不十分重要,可以考慮實現的方便進行選擇。
?三、創建MBean
為了能夠管理Web應用的資源,首先要使資源能夠被管理,按照JMX規范的要求,我們將資源封裝成MBean,實際上也就是為Web應用添加可管理性。
獲得MBeanServer的實例以后,就可以編寫自己的MBean了,根據JMX的規范,MBean有標準的和動態的兩種主要類型,這里就不贅述了,具體可以參看JMX的規范(http://)。有兩種用于特殊用途的動態 MBeans:模型MBean和開放MBean。模型 MBean (Modle MBean)提供了"現成的"MBean 實現,您可以使用它來快速地利用任何 JMX 可管理資源。它是預制的、通用的和動態的 MBean 類,并且提供了參考實現,已經包含了所有必要缺省行為的實現 - 允許您在運行時添加或覆蓋需要定制的那些實現。這使得基于 Java 的、非工具化的資源能夠在運行時提供保證兼容的 MBean 虛包,使它們能夠通過 JMX 體系結構進行管理。
在Web應用中,資源是多元化,有運行實例,靜態文件,甚至是設備或者是硬件狀態,那么我們把這些資源可以分為兩類,一些資源需要進行動態的管理監控(如登陸用戶數,數據庫連接監控,即時日志等),一些則是靜態資源,只需要進行靜態的管理(系統配置文件,日志級別等),要管理這些不同類型的資源,這就要求MBean一方面能夠直接調用運行實例提供的接口進,另一方面又希望MBean自身能夠提供靜態資源的管理,模型MBean能夠很好的滿足這樣的要求,并且對于新增需要管理的資源,提供了很好的靈活性和可擴展性,因此推薦模型MBean作為Web應用首選。當然具體采用何種類型的MBean,需要結合不同的應用,或者多種類型的MBean相結合使用。順便提一點,采用不同類型的MBean,對于管理客戶端是透明的,也就是說客戶端使用一致的訪問方法來訪問MBean的屬性和方法,這也是JMX的一個優點。
為了更好的觀察如何實現對靜態資源和動態資源的管理,本文編寫了兩個MBean,分別實現對數據庫連接屬性實現靜態和動態的管理。靜態管理的 MBean的目的是實現對數據庫配置文件修改,這樣當Web應用重啟后,會使用新的數據庫配置,我們采用 MBean的提供對資源的直接操作進行實現。動態管理的MBean目的是對已經實例化的數據庫連接對象的屬性進行修改,這樣在不重新啟動Web應用的情況下,改變數據庫連接。
1、實現管理配置文件的MBean。
JDBCConfig是一個Model MBean,它的作用是對config.properties文件封裝成MBean,該MBean包括四個屬性及其相關的getter和setter和一個save方法,其中save方法的作用是屬性存入配置文件,從而實現了通過MBean直接對配置文件進行操作。這個例子中的MBean繼承了 BaseModelMBean類,該類實現了javax.management.ModelMBean接口,我們可以在commons- modeler.jar中找到BaseModelMBean類,commons-modeler.jar是tomcat4.x中使用的一個開放源碼的包。
import javax.management.MBeanException;
import javax.management.RuntimeOperationsException;
import org.apache.commons.modeler.BaseModelMBean;
import java.io.*;
import java.util.Properties;
public class JDBCConfigMBean extends BaseModelMBean {?
??? private String driver;
??? private String username;
??? private String password;
??? private String dburl;
??? private static final String CONFIG_FILEPATH =?
???? "c://myweb//conf//config.properties";?
??? /
??? //constructor
??? /
??? public JDBCConfig()
??????????? throws MBeanException, RuntimeOperationsException {
??????? super();
??????? init();
??? }?
??? private void init() {
??????? InputStream in = null;
??????? try {
??????????? Properties prop = new Properties();
??????????? in = new BufferedInputStream(
?????? new FileInputStream(CONFIG_FILEPATH));
??????????? prop.load(in);
??????????? driver = prop.getProperty("driver");
??????????? dburl = prop.getProperty("dburl");
??????????? username = prop.getProperty("username");
??????????? password = prop.getProperty("password");
??????????? in.close();
??????? } catch (Exception e) {
??????????? try {
??????????????? if (in != null)
??????????????????? in.close();
??????????? } catch (IOException e1) {
??????????????? e1.printStackTrace();
??????????? }
??????????? e.printStackTrace();
??????? }
}?
???
??? //getter and setter
???
???
??? //public method
???
??? public String save() {
??????? OutputStream out = null;
??????? try {
??????????? out = new BufferedOutputStream(new?
?????? FileOutputStream(CONFIG_FILEPATH));
??????????? Properties prop = new Properties();
??????????? prop.setProperty("driver", driver);
??????????? prop.setProperty("dburl", dburl);
??????????? prop.setProperty("username", username);
??????????? prop.setProperty("password", password);
??????????? prop.store(out, "---jdbc config---");
??????????? out.close();
??????????? return "save success!";
??????? } catch (Exception e) {
??????????? try {
??????????????? if (out != null)
??????????????????? out.close();
??????????? } catch (IOException e1) {
??????????????? e1.printStackTrace();
??????????? }
??????????? e.printStackTrace();
??????????? return "save error!";
??????? }
??? }
}
2、實現修改運行實例的屬性。
上面的例子是對靜態資源的修改,如果需要對正在運行的類實例進行動態修改, Model MBean同樣提供了很好的方法,在javax.management.ModelMBean的API中有個setManagedResourced方法,這個方法的作用是將正在運行的類實例置入Model MBean,實際上所要做的也就這么多。當需要對改變實例屬性的時候,只需要調用MBean的setAttribute的方法即可,MBean將會通過反射調用該實例對應的屬性設置方法,就是這么簡單。但是許多人喜歡直接從代碼了解過程,因此我將如何實現動態修改實例的屬性代碼也寫出來。同樣以JDBC為例,這里假設系統已經有了一個使用JDBC的數據庫連接,現在需要改變JDBC的屬性,并建立新的連接,這一切都在不重啟Web應用系統的情況下完成。
第一段代碼是Web應用使用的數據庫連接類,它提供一個獲得數據庫連接,和測試連接屬性的兩個方法,以及相關的屬性。當Web應用啟動時,該類將會實例化,我們暫且把這個實例叫資源實例。
package com.myApp.db;
import java.sql.Connection;
import java.sql.SQLException;
public class DBAccess {
??? private String driver;
??? private String username;
??? private String password;
??? private String dburl;
??? public Connection getConnection()
??? {
?.....
??? }?
??? public static boolean testConnection(String driver,String username,
???? String password,String dburl)
??? {
??? }
???
??? //getter and setter
???
}
第二段代碼是一個Model MBean類,和前面提到的第一個Model MBean一樣,繼承了BaseModelMBean,不同的是它將擁有Web應用運行時期資源實例的引用,因為所有的屬性和方法都在所引用的資源實例中,父類BaseModelMBean完成了通過反射調用該資源實例對應的屬性設置方法,所以這個類的編寫相當簡單,我們可以在里面完成其他一些相關的方法。
package com.myApp.db;
import org.apache.commons.modeler.BaseModelMBean;
import javax.management.MBeanException;
import javax.management.RuntimeOperationsException;
public class ResInstanceMBean extends BaseModelMBean {
??? public ResInstanceMBean ()
??????????? throws MBeanException, RuntimeOperationsException {
??????? super();
??? }
??? /** other operations **/
??? .....
}
第三段代碼是如何實現管理數據庫連接類的運行實例。
/**Web應用運行初期創建的數據庫連接**/
DBAccess dbAccess = new DBAccess();
dbAccess.setDriver();
dbAccess.setPassword();
dbAccess.setUsername();
dbAccess.setDburl();
......
/** 創建并注冊管理數據庫的MBean
//創建mbean
ResInstanceMBean mbean= new ResInstanceMBean();
//設置MBeanInfo,這是必須的
mbean.setModelMBeanInfo(createMBeanInfo());
//設置MBean所管理的資源實例,instance為數據庫連接的實例,
//"objectReference"是必須的,否則將無法將資源實例設置到MBean中,記住就行了
DBAccess instance = getDBAccess();
mbean.setManagedResource(instance, "objectReference");
//注冊mbean到MBean Server中
MBeanServer serv = getMBeanServer();
ObjectName oname = createObjectName(mbean);
serv.registerMBean(mbean, oname);
四、創建MBean描述文件
在上面第三段代碼中,我們可以看到,要將MBean注冊到MBean Server中必須先創建MBeanInfo,MBean的setModelMBeanInfo()用來將MBeanInfo設置到MBean中。為了能夠靈活的獲得MBean的信息,從而將MBean注冊到MBeanServer,在O'Reilly出版的"java enterprise的最佳實踐"里提到,采用XML文件對MBean描述是一種非常不錯的選擇方案,并且提供了一個XML描述范例,因此本文也推薦在管理Web應用也采用使用MBean描述文件的方法。實際上無論tomcat4.X,還是JBOSS,都采用使用MBean描述文件的方式創建MBean,下面提供了一個Tomcat4.x里面的MBean描述文件方案,并用該方案描述了上述提到的兩個數據庫連接管理的MBean。Tomcat提供了讀取該描述文件的辦法,具體可以參看Tomcat提供的幫助文檔--如何使用MBean descriptor ( "http://jakarta.apache.org/tomcat/tomcat-4.1-doc/mbeans-descriptor- howto.html")。
<mbean-list>
<mbean name="JDBCConfigMBean"
?????? className="com.myApp.jmx.JDBCConfigMBean"
?????? description="the object to access database"
?????? domain="myapp">
??? <attribute?? name="driver"
?????? description="Jdbc driver name"
?????? type="java.lang.String"
?????? writeable="false"/>
??? <attribute?? name="dburl"
?????? description="database url"
?????? type="java.lang.String"/>
??? <attribute name="username"
?????? description="Database user name"
?????? type="java.lang.String"/>
??? <attribute name="password"
?????? description="vthe user name's password"
?????? type="java.lang.String"/>
??? <operation name="save"
?????? description="save the configuration"
?????? impact="ACTION"
?????? returnType="java.lang.String">
??? </operation>
? </mbean>
<mbean name="DBAccess"
?????? className="com.myApp.jmx.ResInstanceMBean"
?????? description="the object to access database"
?????? domain="myapp"
?????? type="com.myApp.db.DBAccess">
??? <attribute name="driver"
?????? description="Jdbc driver name"
?????? type="java.lang.String"
?????? writeable="false"/>
??? <attribute name="dburl"
?????? description="database url"
?????? type="java.lang.String"/>
??? <attribute name="username"
?????? description="Database user name"
?????? type="java.lang.String"/>
??? <attribute name="password"
?????? description="vthe user name's password"
?????? type="java.lang.String"/>
??? <operation name="testConnection"
?????? description="test configure attribute"
?????? impact="ACTION"
?????? returnType="java.lang.String">
??? <parameter name="driver"
?????? description="Jdbc driver name for test"
?????? type="java.lang.String"/>
??? <parameter name="username"
?????? description="Database user name for test"
?????? type="java.lang.String"/>
??? <parameter name="password"
?????? description="the user name's password for test"
?????? type="java.lang.String"/>
??? <parameter name="dburl"
?????? description="database url for test"
?????? type="java.lang.String"/>
??? </operation>
? </mbean>
</mbean-list>
五、注冊MBean
在對MBean注冊前,必須得到MBean的描述信息,并且保存在 MBeanInfo的實例中,否則是無法將MBean注冊到MBean Server當中的,通過MBean描述文件,獲得各種類型MBean的描述信息是一件非常簡單的事情,而這些正是創建MBean所需要的,這樣做的優點在于不需要通過編寫代碼,只需要修改描述文件,就可以添加新的MBean,注冊的代碼實際上我們之前的代碼已經列出。在MBean注冊時必須指定對應的 ObjectName,ObjectName相當于MBean在MBean Server中的唯一名字,它的格式為:"domain:key1=value1,key2=value2...",可根據系統的要求定義一套命名的規則。
//注冊mbean到MBean Server中
MBeanServer serv = getMBeanServer();
ObjectName oname = createObjectName(mbean);
serv.registerMBean(mbean, oname);
六、編寫管理框架的客戶端
我們已經完成了服務器端MBean的注冊工作,接下來是如何讓用戶能夠使用這些MBean管理資源。雖然JMX的參考實現中提供了 HTMLAdapter,使用戶能夠通過瀏覽器使用MBean。但是提供的界面并不是那么友好可親,一向苛刻的客戶對這絕對不會滿意的。因此,編寫一些簡潔的訪問MBean頁面還是有必要的。如何通過java訪問MBean,可以參閱JMX的資料,這些資料非常多。
根據上面的介紹,如果要增加對Web應用的管理功能或管理系統,基于JMX的管理框架絕對是一個非常明智的選擇。
總結
以上是生活随笔為你收集整理的为Web应用建立基于JMX的管理系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是JMX?
- 下一篇: PowerDesigner导入SQL生成