維護複雜的遺留系統是一項挑戰,而文檔、理性設計以及編碼實踐的缺乏通常會使情況變得更為糟糕。遺憾的是,幾乎所有的軟體開發人員在其職業生涯中都會遇到此類任務。
對於任何使用資料庫的應用程式,跟蹤應用程式所產生的SQL語句是非常有益的。這樣的跟蹤有助於分析效能瓶頸和調試錯誤,還可以協助開發人員瞭解與應用程式相關的商務程序。
對於遺留的應用程式,我們希望可以進行這樣的跟蹤而不必修改任何代碼或應用程式配置。利用WebLogic的JMX API,我們可以快速地編寫出跟蹤大型複雜遺留應用程式的JDBC調用的少量代碼,而不會對現有代碼和應用程式配置產生影響。此外,這種小項目有助於我們理解JMX以及在幕後WebLogic是如何使用JMX的。在本文中,我將展示利用WebLogic JMX跟蹤SQL語句的細節。
什麼是JMX?
JMX全稱為Java Management Extensions(Java管理擴充)。MBean(即managed bean,託管bean)是可以通過JMX API進行管理的資源。大多數應用伺服器使用JMX來提供管理主控台並管理資源。此外,應用程式開發人員可以在他們的定製應用程式中使用JMX來提供管理和審計功能。
WebLogic的JMX實現為開發人員和管理員提供了哪些優點?
WebLogic Server使用JMX MBeans進行配置和管理。每個WebLogic Server會有一個自己的MBean的副本,它由管理伺服器負責更新。管理伺服器維護它所管理的所有伺服器的MBeans的正本。一旦管理伺服器發生故障,託管伺服器將根據本地的MBean副本運行,直到管理伺服器可以再次更新該伺服器的本地MBean。
WebLogic不僅提供了一個使用JMX MBeans的管理主控台,它還提供了一個API以便允許應用程式開發人員配置和研究WebLogic資源。利用WebLogic JMX的最容易的方式就是使用WebLogic控制台來更改WebLogic資源的配置,以及查看控制台中的技術指標。雖然WebLogic控制台的監控和配置功能相當強大,可以滿足運行在WebLogic上的大多數應用程式的需要,WebLogic JMX API還是提供了一種更為強大的工具來管理運行在WebLogic平台上的應用程式。WebLogic JMX API的使用使得配置和擴充WebLogic資源成為可能,還可以從WebLogic的子系統接收通知。例如,一個JDBC串連的最小和最大數設為n的應用程式可能希望有一個監聽器,以便監聽來自WebLogic JMX MBeans的通知,並且在有n-x個並發JDBC串連使用應用程式時,會向管理員寄送電子郵件,從而使管理員可以決定增加n值並重新設定JDBC串連池(這裡x是一個由管理員決定的任一數字)。應用程式開發人員進一步使用JMX的例子包括WebLogic子系統中的跟蹤事件,包括EJB事件和伺服器啟動/停止事件。
在分析JDBC語句方面,WebLogic應用程式中有哪些可用選項?
在WebLogic應用程式中,有多種技術可以用來建立對JDBC語句的動態跟蹤。為來自java.sql包的Statement、PreparedStatement和CallableStatement類建立子類,以便使用Log4J或WebLogic記錄之類的記錄系統列印跟蹤資訊,然後在應用程式中使用這些子類,這是一個可行方案,但是並不適用於遺留代碼。也可以使用類似於TOAD的工具來實現這種跟蹤,但是此類工具對於應用程式開發人員而言可能不容易得到,而且可能無法提供所需的全部資訊。AOP技術是列印JDBC語句的另一種可行方案。然而在撰寫本文時,BEA WebLogic還沒有正式支援AOP,儘管關於WebLogic AOP的文章已經在dev2dev網站上出現。在撰寫本文時,在WebLogic上實現AOP也並不是一項輕而易舉任務。使用WebLogic 6.1或8.1的WebLogic JMX不需要使用任何附加的類庫和配置,因為所需的所有類均可在weblogic.jar中得到,而且代碼實現起來相當簡單。況且WebLogic JMX是一項非常成熟的技術,可以通過不改變任何核心應用程式代碼或者位元組碼來實現。
使用WebLogic JMX API
WebLogic javadoc可以通過http://e-docs.bea.com/wls/docs81/javadocs/線上獲得。該API包括幾個名稱中包含management的包,這些包就是WebLogic的JMX實現(參見表1)。
使用JMX跟蹤JDBC調用
一種編寫跟蹤代碼並提供一個使用者介面來查看SQL的簡單方法是編寫一個JSP、一個Servlet以及一個Java Bean或對象。我們將展示bean/POJO的全部細節,而省去使用者介面/控制器方面的大多數細節,因為大多數WebLogic開發人員對此已有很深的瞭解。注意,無需修改任何部署描述符、資料庫連接池或資料來源來實現跟蹤,所有對應用程式的更改將在運行時進行。
步驟1
首先我們將建立一個名為MyTracerBean.java的類,並匯入所需的WebLogic JMX包和類。
import javax.naming.Context;import weblogic.jndi.Environment;import weblogic.management.MBeanHome;import weblogic.management.configuration.JDBCConnectionPoolMBean;import weblogic.management.runtime.JDBCStatementProfile;import weblogic.management.runtime.JDBCConnectionPoolRuntimeMBean;import javax.management.InstanceNotFoundException;import javax.management.InvalidAttributeValueException;import javax.naming.NamingException;
這些類均位於weblogic.jar中,因此不需要向WebLogic類路徑添加任何JAR或類。
步驟2
接下來我們將編寫一個擷取MBeanHome的方法。
private MBeanHome getMBeanHome() {//URL to the serve whose JDBC activity we are tracingString url = "t3://localhost:7001"; String username = "mywlconsoleuname"; String password = "mywlconsolepsswd";//The MBeanHome will allow us to//retrieve the MBeans related to JDBC statement tracingMBeanHome home = null;try { //We'll need the environment so that we can //retrieve the initial context Environment env = new Environment(); env.setProviderUrl(url); env.setSecurityPrincipal(username); env.setSecurityCredentials(password Context ctx = env.getInitialContext();//Retrieving the MBeanHome interface for the server with //the url t3://localhost:7001 home =(MBeanHome)ctx.lookup(MBeanHome.LOCAL_JNDI_NAME); } catch (NamingException ne) { System.out.println("Error getting MBeanHome " + ne); } return home;}
對於最簡單的情形:管理伺服器也駐留了我們要跟蹤的JDBC應用程式,上述代碼完全可行;但是對於管理伺服器獨立於託管伺服器,並且涉及到幾個獨立JVM的情形,我們需要獲得管理MBeans home而不是本地MBeans home。二者的區別在於,本地home只為單個伺服器提供Mbean,而管理home則為管理伺服器所管理的所有伺服器提供MBean。為了獲得管理MBeans home而不是本地MBeans home,可以將上述代碼中的LOCAL_JNDI_NAME替換為ADMIN_JNDI_NAME。
步驟3
提供一種開啟和關閉JDBC分析的方式是非常有用的,因為分析的開銷相當大,所以不需要時應當將其關閉。預設情況下,分析是關閉的,因此必須在跟蹤任何JDBC語句前開啟它。建立一個如下的方法:
public void configureJDBCAuditing(boolean isOn) { try { MBeanHome home = getMBeanHome(); //Retreive the bean to help us configure the PoolJDBCConnectionPoolMBean mConfigBean =(JDBCConnectionPoolMBean)home.getConfigurationMBean("MyPool","JDBCConnectionPoolConfig"); mConfigBean.setSqlStmtProfilingEnabled(isOn); mConfigBean.setSqlStmtParamLoggingEnabled(isOn); } catch (InvalidAttributeValueException iave) { System.out.println("Invalid attribute while configuring tracing " + iave); } catch (InstanceNotFoundException infe) { System.out.println("Instance not found while configuring tracing " + infe); }}
在上述代碼中,我們還告知串連池我們希望查看傳入SQL語句中的參數。這會增加跟蹤的開銷,但是它可以為我們提供一些有價值的資訊。
步驟4
在配置JDBC池來儲存設定檔之後,我們可以對其進行查詢。記住,檢索到的設定檔數等於開啟分析後所執行的SQL語句數,而不等於所有由MyPool ConnectionPool執行的SQL語句數。以下的代碼檢索設定檔,maxProfiles參數指示應該擷取最近的多少個設定檔。建立的方法如下:
/** Pass in -1 to get all profiles */public JDBCStatementProfile[] getProfiles(int maxProfiles) { JDBCStatementProfile[] profiles = null; try { MBeanHome home = getMBeanHome();JDBCConnectionPoolRuntimeMBean mbean =(JDBCConnectionPoolRuntimeMBean)home.getRuntimeMBean("MyPool","JDBCConnectionPoolRuntime"); int numProfiles = mbean.getStatementProfileCount(); int profilesIndex = 0; //figure out index to start at and how many we want if (maxProfiles != -1) { profilesIndex = numProfiles - maxProfiles;}else { maxProfiles = numProfiles;}profiles =mbean.getStatementProfiles(profilesIndex,maxProfiles); } catch (InstanceNotFoundException infe) {System.out.println("Problem retrieving jdbc profiles " + infe); } return profiles; }
JDBCConnectionPoolRuntimeMBean的getStatementProfiles方法在WebLogic 8.1 API文檔中沒有提及,儘管它曾在WebLogic 6.1文檔中出現。不過這看起來是個錯誤,因為在WebLogic 8.1中該方法是可用的,並且WebLogic 8.1還修複了方法中的一個bug(CR094729,參見http://e-docs.bea.com/wls/docs81/notes/resolved_sp01.html),這意味著WebLogic 8.1是打算包含該方法的。
步驟5
可以添加一個清空功能,使得重啟伺服器時可以清空語句緩衝:
public void reset() { MBeanHome home = getMBeanHome(); try {JDBCConnectionPoolRuntimeMBean mbean =(JDBCConnectionPoolRuntimeMBean)home.getRuntimeMBean("MyPool","JDBCConnectionPoolRuntime");//Remove everything from the cache mbean.resetStatementProfile(); } catch (InstanceNotFoundException infe) {System.out.println("Problem while resetting JDBC profiles " + infe); } }
步驟6
對設定檔進行迭代,獲得要顯示的資訊(參見清單1)。這些代碼可能放在使用者介面層,比如放在一個JSP中。
清單1
MyTracerBean myTracer = new MyTracerBean();//In this case we want the 100 most recently executed statementsJDBCStatementProfile[] profiles = myTracer.getProfiles(100);//Doing the looping so that the most recent statements information is//retrieved firstfor (int i=profiles.length-1;i>-1;i--) {//Getting the number of parameters passed into the current //statementint paramCount = profiles[i].getParameterCount();//Format the start and end time for the current statementSimpleDateFormat simpleDateFormat =new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm:ss:SS aaa");String startTime=simpleDateFormat.format(new Date(profiles[i].getStartTime()));String endTime=simpleDateFormat.format(new Date(profiles[i].getStopTime()));//Append the parameters together in order to display themStringBuffer paramsBuffer = new StringBuffer();if (paramCount < 1) {paramsBuffer.append("None");} else {for (int j=0;j<paramCount;j++) {paramsBuffer.append(profiles[i].getParameter(j));paramsBuffer.append(" ");}}String statementTxt = profiles[i].getStatementText();String paramsTxt = paramsBuffer.toString();String timeTaken = profiles[i].getTimeTaken()//Then use statementTxt, paramsTxt, timeTaken, startTime, endTime// etc to show the statement details in a UI}
展望WebLogic JMX的前景
在撰寫本文時,剛剛發布的WebLogic 9.0支援的是JMX 1.2而不是WebLogic 8.1及以前版本一直支援的JMX 1.0。響應JMX規範的變化,9.0中的WebLogic JMX API有了相當大的變化,清單1中的代碼可能會引起不支援的警告。當升級至9.0時,應當用JDBCDataSourceRuntimeMBean替換JDBCConnectionPoolRuntimeMBean。不過,在撰寫本文時,絕大多數運行在WebLogic上的遺留應用程式還沒有使用WebLogic Server 9.0,而且很可能在相當長的一段時間內不會使用9.0。
原文出處: Custom Debugging with WebLogic JMX http://wldj.sys-con.com/read/138275.htm
| 作者簡介 |
| |
Salma Saad在International Survey Research公司工作,她負責ISR的完善且高可用的全球調查報告網站,在Java及相關技術領域擁有接近十年的專業經驗。 |