程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> AOP@Work: 用AspectJ進行性能監視,第2部分

AOP@Work: 用AspectJ進行性能監視,第2部分

編輯:關於JAVA

通過裝載時織入使用Glassbox Inspector

簡介:有了基本的面向方面的監視基礎架構後,可以對它進行擴展以滿足真 實 世界的監視要求。在這篇由兩部分組成的文章的第二部分,Ron Bodkin 展示了 如 何在 Glassbox Inspector 中添加企業監視功能,包括監視多個應用程序、Web 服務和 Web 應用程序框架。他還展示了如何跟蹤應用程序錯誤並在監視代碼中 包 含它們,並展示了如何以編程方式部署和控制這個監視基礎架構。

我在本文的 第 1 部分 中給出的基本 Glassbox Inspector 只能監視簡單的 、基於 Servlet 的應用程序(如在 Duke 的 Bookstore 這個例子中),更系統 化的框架應包括對流行的 Web 應用程序框架(如 Struts 和 Spring)的支持。 如今的 Web 應用程序很少使用 Servlet 直接處理請求。這些框架通常將所有動 態頁請求委派給網關 Servlet,如 Spring 框架的 DispatcherServlet,然後它 再將請求處理委派給不同的控制器。與此類似,Struts ActionServlet 委派給 作 為控制器的 Action 子類。

在這篇探索用 AspectJ 進行性能監視的文章的第二部分中,我對 Glassbox Inspector 進行擴展,通過增加監視器以提供關於應用程序性能的更有意義的信 息。這些監視器跟蹤 Struts 和 Spring Web 應用程序框架的控制器之間的交互 以及 Web 服務的響應和請求操作。我還擴展了系統以支持多個應用程序,並添 加 了一個錯誤處理層和在運行時方便地啟用和禁止監視的能力。在文章的最後展示 如何用裝載時織入部署 Glassbox Inspector,以及如何測量帶來的開銷。

為了理解本文中的例子,需要對 Spring MVC、Struts 和 Apache Axis for Web Services 有一些了解。Glassbox Inspector 監視基礎架構的完整代碼請參 閱 下載。要下載運行本文中的例子所需要的 Aspectj、JMX 和 Tomcat 5 請參 閱 參考資料。

可重用的操作監視器

為了得到范圍更廣泛的監視功能,我將 第 1 部分的清單 5 中大多數可重用 的 Servlet 監視代碼放到 AbstractOperationMonitor 方面中。很多情況下跟 蹤 嵌套的請求很重要,包括跟蹤在框架不同級別上的控制器以及跟蹤委派的請求( 如從轉發到嵌套請求)的性能。清單 1 顯示了我如何擴展 Glassbox Inspector 以跟蹤對操作的嵌套請求:

清單 1. 可重用的操作監視方面

public abstract aspect AbstractOperationMonitor extends AbstractRequestMonitor { 

 protected abstract class OperationRequestContext extends RequestContext {
   /**
     * Find the appropriate statistics collector object for this
    * operation.
    * @param operation an instance of the operation being
    *     monitored
    */
   public PerfStats lookupStats() {        
     if (getParent() != null) {
       // nested operation
        OperationStats parentStats =
           (OperationStats) getParent().getStats();
       return parentStats.getOperationStats(getKey());
     }
      return getTopLevelStats(getKey());
   }

   /**
     * Determine the top-level statistics for a given operation
     * key. This also looks up the context name for the
    * application from the operation monitor:
    * @see AbstractOperationMonitor#getContextName(Object)
    * For a Web application, top-level statistics are normally
    * all Servlets, and the key is the Servlet name.
    * @param key An object to uniquely identify the
    *     operation being performed.
    */
   protected OperationStats getTopLevelStats(Object key) {
     OperationStats stats;
      synchronized(topLevelOperations) {
       stats = (OperationStats)topLevelOperations.get(key);
       if (stats == null) {        
         stats =
            perfStatsFactory.createTopLevelOperationStats(key,
               getContextName(controller));
          topLevelOperations.put(key, stats);
       }
     }
     return stats;    
   }

   /**
      * @return An object that uniquely identifies the operation
     *     being performed.
    */
   protected abstract Object getKey();  

   /** The current controller object executing, if any. */
   protected Object controller;
 };
 /**
  * This advice stores the controller object whenever we construct a
  * request context.
  */
 after(Object controller) returning (OperationRequestContext ctxt) :
  cflow (adviceexecution() && args(controller, ..) &&
   this(AbstractOperationMonitor)) &&
  call (OperationRequestContext+.new(..)) {
   ctxt.controller = controller;
 }  
...

AbstractOperationMonitor 的第一部分擴展了本文第 1 部分中原來的 Servlet 監視器以查看嵌套操作的統計。使用嵌套操作使我們可以在分派 JSP 或 者分解 Web 服務或者多方法應用程序控制器的不同方法這樣的情況下跟蹤資源 占 用。原來的 lookupStats() 方法現在檢查父請求上下文。如果有父請求,那麼 它 調用新方法 getOperationStats() 以獲取它。否則,它調用新方法 getTopLevelStats(),這個方法調用一個工廠以創建一個新的 OperationStats 。 使用工廠可以保證我的監視基礎架構不依賴於統計類的實現。

監視多個應用程序

在清單 1 中,我還加入了對運行在單個應用服務器中的多個應用程序的監視 支持。主要是通過增加一個檢查應用程序上下文的預查來做到這一點的。在檢查 頂級統計時,我調用一個監視器模板方法 getContextName(),以確定操作關聯 的 是哪一個應用程序。下面將會看到對於 Servlet 它是如何處理的。注意,向 getContextName() 方法傳遞了控制器的一個實例,這樣就可以檢查關聯的應用 程 序上下文。

在抽象操作監視器中,還提供了具有具體建議的切點,它定義了監視請求的 常 用方法。對它做了擴展以便監視收到的請求,如 Struts 操作和收到的 Web 服 務 請求。清單 2 展示了一個代表性的例子:

清單 2. AbstractOperationMonitor 中的監視模板

 /**
  * This defaults to no join points. If a concrete aspect overrides
  * classControllerExec with a concrete definition,
  * then the monitor will track operations at matching join points
  * based on the class of the controller object.
  */
 protected pointcut classControllerExec(Object controller);

 Object around(final Object controller) :
   classControllerExec(controller) {
   RequestContext rc = new OperationRequestContext() {
     public Object doExecute() {
       return proceed(controller);
     }

      protected Object getKey() {
       return controller.getClass();
     }            
     };
   return rc.execute();    
 }  
 // Controller where the name of the signature at the monitored join point
 // determines what is being executed, for example, the method name
 /**
  * This defaults to no join points. If a concrete monitor overrides
  * methodSignatureControllerExec with a concrete
  * definition, then it will track operations at matching join points
  * based on the run-time class of the executing controller instance
  * and the method signature at the join point.
  */
  protected pointcut methodSignatureControllerExec(Object controller);

 Object around(final Object controller) :
   methodSignatureControllerExec(controller) {
   RequestContext rc = new OperationRequestContext() {
     public Object doExecute () {
       return proceed(controller);
     }

     protected Object getKey() {
       return concatenatedKey(controller.getClass(),
         thisJoinPointStaticPart.getSignature().getName());
     }

   };
   return rc.execute();    
 } 

classControllerExec() 的切點捕獲所有類控制器處理請求的點,像 Servlet do 方法執行或者普通 Struts action execute 方法,在這裡響應請求的對象的 類確定要執行的操作。更准確地說, classControllerExec() 切點 定義了一個 空的切點(它不會匹配任何連接點)。然後它提供一個具體建議,這個建議設置 工人對象並返回對於這種情況正確的鍵值。這與使用一個抽象切點類似,其中子 方面必須覆蓋切點以使用建議。不過在這裡,我提供了永遠不匹配的默認定義。 如果 AbstractOperationMonitor 的子方面不需要監視類控制器,那麼它不覆蓋 這個切點就行了。如果它需要監視類控制器,那麼它就提供什麼時候監視一個點 的定義。

具體化操作監視器

methodSignatureControllerExec() 切點和關聯的建議類似:它們提供具體 化 操作監視方面的方法,以根據連接點上的簽名匹配分派到不同方法的控制器。

清單 3 展示了擴展 AbstractOperationMonitor 以監視 Struts 和 Spring MVC 操作的具體方面:

清單 3. 監視 Struts 和 Spring MVC 框架

public aspect StrutsMonitor extends AbstractOperationMonitor {
  /**
    * Marker interface that allows explicitly _excluding_ classes
    * from this monitor: not used by default. If using Java™ 5, an
   * annotation would be better.
   */
  public interface NotMonitoredAction {}
 /**
  * Matches execution of any method defined on a Struts action or
  * any subclass, which has signature of an action execute (or
  * perform) method, including methods dispatched to in a
  * DispatchAction or template methods with the same signature.
  */
 public pointcut actionMethodExec() :
   execution(public ActionForward Action+.*(ActionMapping,
       ActionForm, ServletRequest+, ServletResponse+)) &&
   !within (NotMonitoredAction);
 /**
  * Matches execution of an action execute (or perform) method for
  * a Struts action. Supports the Struts 1.0 API (using the perform
  * method) as well as the Struts 1.1 API (using the execute method).
  */
 public pointcut rootActionExec() :
   actionMethodExec() && (execution(* Action.execute(..)) ||
   execution(* Action.perform(..)));
  
 /** @Override */
  protected pointcut classControllerExec(Object controller) :
     rootActionExec() && this(controller);
 protected pointcut dispatchActionMethodExec() :
   actionMethodExec() && execution(* DispatchAction+.*(..));
 protected pointcut methodSignatureControllerExec(Object controller):
    dispatchActionMethodExec() && this(controller);
}
public aspect SpringMvcMonitor extends AbstractOperationMonitor {
 /**
  * marker interface that allows explicitly _excluding_ classes
  * from this monitor: not used by default
  */
 public interface NotMonitoredController {}
  
 public pointcut springControllerExec() :
   execution(public ModelAndView Controller+.*(HttpServletRequest,
        HttpServletResponse)) &&
   !within (NotMonitoredController+);
 protected pointcut classControllerExec(Object controller) :
    springControllerExec() && execution(* handleRequest(..)) &&
   this(controller);
 protected pointcut methodSignatureControllerExec(Object controller):
    springControllerExec() &&
   execution(* MultiActionController+.*(..)) && this(controller);  
}

關於這兩個方面首先要注意的是它們非常簡潔。它們只是擴 展 了操作監視器中的兩個切點以具體地監視它們特定的 API。因為它們的簡潔性, 也可以在 XML 中定義這兩個具體工作監視器,而不用編譯。關於 AspectJ 5 的 這個功能的例子請參閱 清單 10。

關於 StrutsMonitor 和 SpringMvcMonitor

清單 3 中的 StrutsMonitor 方面設計為同時使 用 老版本和新版本的 Struts API: Struts 1.0 操作是通過調用 perform() 而調 用的,而 Struts 1.1 操作是通過調用 execute() 而調用的。我將正常操作類 的 具體子類作為 Class 控制器跟蹤:只有執行對象的類才是關注的。不過,如果 一 個類擴展了 DispatchAction,Struts 可以讓控制器分派給這個類中的多個方法 。我通過匹配 DispatchAction 子類中所有具有 Struts 操作簽名的方法監視在 這些分派操作中執行的各個方法。這種方式使我可以分別跟蹤每一個不同控制器 方法的統計。

我用 iBatis JPetStore 1.3 示例應用程序(請參閱 參考資料) 測試了 StrutsMonitor。與許多應用程序一樣,它用自己的一個小 框 架擴展了 Struts:公共基本操作有一個名為 perform() 的方法,它向 helper 分派用 doPerform() 作為模板方法的操作。不過,不需要跟蹤這些模板方法的 執 行:類級別的控制器會識別在 execute() 方法中執行的 Action 的特定子類, 這 足以區分它們了。

SpringMvcMonitor 方面與 StrutsMonitor 有一 點 很類似,它們將所有 Controller 對象作為類控制器,監視它什麼時候執行 handleRequest()。它還監視 MultiActionController 或者它的任何子類中具有 Spring 控制器方法簽名的公共方法的執行。例如,我在這段代碼中分別監視 welcomeHandler() 和 ownerHandler() 的執行:

public class ClinicController extends MultiActionController
 implements InitializingBean {
...
 public ModelAndView welcomeHandler (HttpServletRequest request,
  HttpServletResponse response) throws ServletException {
   return new ModelAndView ("welcomeView");
 }
...
 public ModelAndView ownerHandler(HttpServletRequest request,
  HttpServletResponse response) throws ServletException {
    Owner owner = clinic.loadOwner(
     RequestUtils.getIntParameter(request, "ownerId", 0));
...
    model.put ("owner", owner);
    return new ModelAndView ("ownerView", "model", model);
 }
...

當然,可以容易地擴展這種方法以處理其他的框架,包括自定義框架,甚至 前 面描述過的用 XML 定義的方面。

編寫可移植的方面

注意 StrutsMonitor 方面像我的大多數監視方面一樣,設計為針對常見組件 的多個版本。它的切點匹配不同版本的 Struts,它們可以引入那些在織入時( 例 如在裝載時)還不可用的類型。只需在像字段簽名或者類型模式這樣的靜態可解 析的上下文中使用類名就可得到這樣的切點。如果在動態上下文中(如 this、 target 或者 args)中使用類名,那麼就要求織入器能夠訪問類型。注意 Struts 監視器將 this 切點指示符綁定到 Object 的一個實例,這不需要有 Struts JAR 。雖然有些別扭(因為它失去了編譯時類型檢查的好處),但是這種方法對於部 署可重用方面時要求有許多依賴庫的情況更有利。

不過,引用類或者訪問其成員的(如通過調用一個方法)的建議或者其他可 執 行代碼需要遵守 Java 代碼的常規可移植性規范。如果建議必須調用在 API 的 不 同版本中有變化的方法,那麼可以使用 Java 反射檢查是否有不同的方法並執行 它們,例如,對一個操作方法顯式地調用 perform() 或者 execute()。注意, 如 果只想在一個 around 建議中的連接點處使用原來的 execute() 或者 perform () 方法,那麼 AspectJ 會替我處理這種情況,不需要做任何特別的工作。實際上 , 許多 API 都提供了向後兼容性,因此可以針對要支持的老版本編譯,它在新版 本 中也能工作。

通常,方面不會執行觸發裝載一個類的代碼,除非程序的執行總是會裝載這 個 類,例如,引用一個類的建議在這個類要執行時會被觸發。保留這個屬性對於涉 及可選類的庫方面很重要。對於 Glassbox Inspector,我不想讓被監視的應用 程 序加入可能監視的某個版本的 Struts 或者其他庫。

監視 JSP 和應用程序名

清單 4 展示了重構後的本文第 1 部分中的 ServletMonitor,它使用抽象操 作監視器作為一個基本方面。我通過擴展 classControllerExec() 繼續按類跟 蹤 Servlet。增加了通過監視 jspService() 方法對監視 JSP 的支持。因為我想要 確定 Web 應用程序的名字,因此設置 ServletMonitor 覆蓋 getContext() 方 法 以檢查 Servlet 上下文的名字。

我到目前為止所實現的所有其他 Web 應用程序最終是由 Servlet 調用的, 因 此只有這些控制器需要提供應用程序上下文的一個實現。特別是,我的 Web 應 用 程序框架監視器是嵌套在它們的分派 Servlet 調用中的。在進一步擴展 Glassbox Inspector 以監視其他操作(如收到的 Web 服務請求)時,將需要提 供操作名作為不同應用程序的上下文。如果要擴展框架以監視收到的 JMS 消息 或 者 EJB 請求,那麼這個框架還要為那些資源提供應用程序上下文。

清單 4. 擴展 ServletMonitor

public aspect ServletMonitor extends AbstractOperationMonitor {

 /**
  * Execution of any Servlet method: typically not overridden in
  * HttpServlets.
  */
 public pointcut ServletService(Servlet Servlet) :
   execution(void Servlet.service(..)) && this(Servlet);

 /** Execution of any Servlet request methods. */
 public pointcut httpServletDo (HttpServlet Servlet) :
   execution(void HttpServlet.do*(..)) && this(Servlet);

 /** Execution of any JSP page service method. */
 public pointcut jspService(JspPage page) :
   execution(* _jspService(..)) && this(page);

  protected pointcut classControllerExec(Object controller) :
    (ServletService(*) || httpServletDo(*) || jspService(*)) &&
   this(controller);
 /**
  * Create statistics for this object: looks up Servlet context to
  * determine application name.
  */
 protected String getContextName(Object controller) {
   Servlet Servlet = (Servlet)controller;
   return Servlet.getServletConfig().getServletContext().
        getServletContextName();
 }
}

更新 JMX 以得到嵌套的統計

在 第 1 部分中,我擴展了 Glassbox 監視基礎架構以提供嵌套的統計,如 Servlet 請求的連接中的 JDBC 語句。在這裡,我分析了如何更新 StatsJmxManagement 方面以提供這些嵌套統計的復合名。我還展示如何將應用 程 序名加入到這個復合名中。例如,數據庫語句的統計可以用以下字符串命名:

application=Spring Petclinic,operation=org.springframework.samples.
petclinic.web.Clin i cController,database=jdbc:hsqldb:hsql://localhost:
9001,statement=S E LECT id;name from types ORDER BY name

這個字符串使用給定信息的所有前級統計的性能統計的描述和名字。以這種 方 式命名統計使 JMX 工具可以自然地將相關的信息組織並顯示在一起。像 JConsole 這樣的 JMX 工具使用結構化的名字來組織常見的元素,更容易以層次 化的方法浏覽,如 圖 1 所示。清單 5 展示了為支持這個功能需要對 StatsJmxManagement 進行的更新;

清單 5. 更新 StatsJmxManagement 以支持嵌套的統計

public aspect StatsJmxManagement {
private String PerfStats.cachedOperationName;
/** JMX operation name for this performance statistics bean. */
public String PerfStats.getOperationName() {
// look up from cache
// else
...
appendOperationName(buffer);
...
return operationName;
}
/** Add bean's JMX operation name to a StringBuffer. */
public void PerfStats.appendOperationName(StringBuffer buffer) {
if (cachedOperationName != null) {
buffer.append(cachedOperationName);
} else {
aspectOf().nameStrategy.appendOperationName(this, buffer);
}
}
public void PerfStats.appendName(StringBuffer buffer) {
// append the appropriate name & JMX encode
...
}
public StatsJmxNameStrategy getNameStrategy() {...}

public void setNameStrategy(StatsJmxNameStrategy nameStrategy) { ... }
private StatsJmxNameStrategy nameStrategy;
}
public interface StatsJmxNameStrategy {
void appendOperationName(PerfStats stats, StringBuffer buffer);
}
public class GuiFriendlyStatsJmxNameStrategy extends
AbstractStatsJmxNameStrategy {
public void appendOperationName(PerfStats stats,
StringBuffer buffer) {
PerfStats parent = stats.getParent();
if (parent != null) {
appendOperationName(parent, buffer);
buffer.append(',');
else {
if (stats instanceof OperationStats) {
OperationStats opStats = (OperationStats)stats;
String contextName = opStats.getContextName();
if (contextName != null) {
buffer.append("application=\"");
int pos = buffer.length();
buffer.append(contextName);
JmxManagement.jmxEncode(buffer, pos);
buffer.append("\",");
}
}
}
buffer.append(stats.getDescription());
buffer.append('=');
stats.appendName(buffer);
}
}

清單 5 展示了我如何設置 StatsJmxManagement 以允許不同的命名策略。為 了使用像 JConsole 這樣的 GUI 工具,我列出了一個 GuiFriendlyStatsJmxNameStrategy,它返回如清單 5 和圖 1 所示的名字。除 此 之外,完整的代碼包括另一個可選的 CanonicalStatsJmxNameStrategy,它遵守 JMX 建議的 MBean 命名方式,但是在我所試過的所有 JMX 工具中不能顯示。

用於構建這些名字的基本方法是,創建一個字符串緩沖區並遞歸地得到父統 計 的操作名,然後在後面加上這個名字。對於頂級操作統計,首先加入上下文(應 用程序)名。為了支持這種方法,我在 PerfStats 中增加了 getDescription() 方法。還更新了操作和資源統計實現以設置合適的值(如 operation, operation1, .., resource, and request)。下載 Glassbox Inspector 源代 碼 以查看所有細節和另一個可選的 CanonicalStatsJmxNameStrategy。

監視 Web 服務調用

Glassbox Inspector 框架現在很容易擴展以支持新類型的請求(操作)以及 新資源。監視提供的(收到的)遠程服務操作與監視 Web 應用程序框架很相似 : 本文的源代碼給出了監視用 Apache Axis 實現的服務的一個例子。

只需要很少的調整就可以將這個 Glassbox Inspector 擴展為監視應用程序 之 外的遠程服務的使用。隨著具有面向服務體系結構的應用程序越來越常見,跟蹤 外部控制的服務的可靠性及確定應用程序問題的相互關系越來越重要了。將這些 數據與應用程序上下文(如執行的 Web 操作和在其他資源上花費的時間)相關 聯 是迅速找出應用程序故障根源的重要方法。這種分析與使用 SOAP 處理程序孤立 地監視 Web 服務調用有很大不同,因為它將實現信息與外部請求的性能連接到 一 起了。

清單 6 展示了一個遠程調用監視器,它監視通過 JAX-RPC API 對 Web 服務 的調用以及 RMI 調用(它本身通常是遠程的):

清單 6. 監視遠程調用

public aspect RemoteCallMonitor extends AbstractResourceMonitor {
 /** Call to remote proxy: RMI or JAX-RPC */
 public pointcut remoteProxyCall(Object recipient) :
   call(public * Remote+.* (..) throws RemoteException) &&
   target(recipient) && !within(glassbox.inspector..*);

 /** Monitor remote proxy calls based on called class and method */
 Object around (final Object recipient) :
  remoteProxyCall(recipient) {
    RequestContext requestContext = new ResourceRequestContext() {

     public Object doExecute() {
       return proceed(recipient);
     }

     public PerfStats lookupStats() {
       String key = "jaxrpc:"+recipient.getClass().getName()+
         "."+thisJoinPointStaticPart.getSignature().getName();
         key = key.intern();

       return lookupResourceStats (key);
     }
   };
   return requestContext.execute();
 }
}

注意,只需要做很少的工作就可以支持這種功能,大多數支持包含在 AbstractRequestMonitor 中。我只是定義遠程代理調用為對任何實現了 Remote 接口的公共方法的調用,這個方法可以拋出 RemoteException。完成這個小小的 改變後,就可以使用 Worker Object 模式(請參閱 第 1 部分)監視遠程調用 , 提供一個以類名和被調用的對象的方法名為基礎的名字。RemoteCallMonitor 方 面使用一個 helper 方法尋找頂級資源統計,它是從 JdbcConnectionMonitor 中 的工人對象中提取的,放入這個新的公共基本方面:AbstractResourceMonitor 。

自然,可以進一步擴展這種方法以監視其他 Web 服務調用以及流行框架(如 Apache Axis)上方法的執行。我還可以使用這種技術監視 XML 處理所花費的時 間,這對於任何有大量 XML 的應用程序是很有用的。許多利用 Web 服務的應用 程序也可受益於這種監視。

監視故障

合乎邏輯的下一步是擴展 Glassbox Inspector 系統以監視應用程序故障。 我 首先在系統退出一個監視點時通過拋出一個異常(更准確地說是任何 Throwable )來記錄故障。故障記錄為跟蹤 Web 操作和數據庫連接工作提供了有用的信息 : 這些操作中的任何一個拋出 Throwable 在幾乎任何情況下都表示有問題。對於 JDBC 語句和其他資源訪問,throwable 通常表明一個真正的應用程序故障,但 是 在有些情況下,它是正常程序邏輯的一部分。例如,試圖用一個已經有的名字注 冊會在插入時觸發異常。為了照顧到這種情況,我對每個監視使用了一個可配置 的策略,以確定給定的 throwable 是真正的錯誤還是作為正常的退出條件接受 。

清單 7 包含了使我可以配置 AbstractRequestMonitor 以確定 Throwable 是 否為故障的建議和改動:

清單 7. 增加可擴展的故障檢測

public aspect AbstractRequestMonitor {
 /**
  * Record an error if this request exited by throwing a
  * Throwable.
  */
 after(RequestContext requestContext) throwing (Throwable t) :
  requestExecution(requestContext) {
   PerfStats stats = requestContext.getStats();
   if (stats != null) {
      if (failureDetectionStrategy.isFailure(t,
          thisJoinPointStaticPart)) {
        requestContext.recordFailure(t);
     } else {
        requestContext.recordExecution();
     }
   }
 }
 public void setFailureDetectionStrategy(...) { ... }
 public FailureDetectionStrategy getFailureDetectionStrategy() { ... }
  protected FailureDetectionStrategy failureDetectionStrategy =
     new FailureDetectionStrategy() {
    public boolean isFailure (Throwable t, StaticPart staticPart) {
      return true;
    }
   };
...
} 
public interface FailureDetectionStrategy {
 boolean isFailure(Throwable t, StaticPart staticPart);
}

清單 7 中最後的效果是,在通過拋出異常退出一個監視的連接點時記錄一個 計數。注意,我肯定是要識別那些通過拋出 Error 退出的情況,因為它們幾乎 肯 定是故障!在這裡,可以容易地擴展這個 AbstractRequestMonitor 以跟蹤部分 或者所有異常、堆棧蹤跡和在發生異常時當前對象和參數值。如果這樣做了,那 麼就需要有一種保證不記錄敏感信息,如密碼、信用卡號或者個人身份信息的方 法。為了完成這種集成,我對 AbstractRequestMonitor 做了小小的修改。細節 請參閱 文章源代碼。

對錯誤使用異常轉換

基於前面的討論,您可能奇怪如何能配置 Glassbox Inspector 以識別出某 些 throwable 不是錯誤。最容易的方法是傳遞一個對於給定監視方法(如 JdbcStatementMonitor)總是返回 false 的策略。另一種簡單的解決方案是檢 測 某種特定的異常類型,如 NumberFormatException,並指明它不是故障。這可以 結合 Exception Conversion 模式(請參閱 參考資料)以將某些異常轉換為有 意 義的層次結構。清單 8 就是如何使用異常轉換以及故障檢測策略的一個示例:

清單 8. 跟蹤故障並使用異常轉換

/**
* Softens and uses Spring's exception translator to convert from
* SQLException to unchecked, meaningful exception types
*/
aspect DataAccessErrorHandling {
 declare soft: SQLException: jdbcCall();
 after() throwing (SQLException e) : jdbcCall() {
   throw sqlExceptionTranslator.translate("dbcall", getSql(), e);
 }
 declare precedence: ModelErrorHandling, JdbcStatementMonitor;
}
/**
* Conservative detection strategy: failures from SQL that often
* indicate valid business conditions are not flagged as failures.
*/
public class DataAccessDetectionStrategy implements FailureDetectionStrategy {
  public boolean isFailure(Throwable t, StaticPart staticPart) {
    return !(t instanceof ConcurrencyFailureException) &&
       !(t instanceof DataIntegrityViolationException);
 }
}

清單 8 的第一部分顯示了一個簡單的錯誤處理方面,它軟化了來自 JDBC 調 用的 SQLExceptions,然後使用一個 Spring 框架 SQLExceptionTranslator( 如 SQLErrorCodeSQLExceptionTranslator)將異常轉換為有意義的(unchecked) 異 常。這個方面還用 AspectJ 的 declare precedence 格式聲明優於 JdbcStatementMonitor。這保證了在 JdbcStatementMonitor 跟蹤返回的異常類 型之前,DataAccessErrorHandling 已經轉換了異常。

清單 8 的其他部分展示了一個示例策略,它只在幾乎確實表明有故障(如無 法訪問資源)的條件下表明故障。特別是,它排除了並發錯誤(在設計良好的、 多用戶應用程序中會出現)和數據完整性破壞(如通常在注冊新用戶時發生)的 情況。

要對故障檢測有更多的控制,可能要有一個標志性接口或者一個方法或者類 上 的注釋,以表明在正常的操作過程中,系統的某些部分會拋出 Exception。這可 結合使用 declare parents 或者 AspectJ 5 新的 declare annotation 格式捕 獲模塊化規則,表明拋出一個 Throwable 是否表明故障。在大多數情況下,我 使 用的簡單規則對於粗粒度的監視是合適的,但是要避免不正確的警報,最好有更 多的靈活性。

檢測常見 Web 故障

應用程序有可能在出現故障時並不拋出異常。一種最常見的情況是當控制器 處 理異常並將請求轉發給一個錯誤頁時。另一種情況是當對於頁的 HTTP 響應指明 一個錯誤時。清單 9 更新了清單 4 中的 ServletMonitor 以檢測這種情況。我 還對 AbstractRequestMonitor.RequestContext 工人對象做了小小的修改以設 置 錯誤上下文,並在設置錯誤上下文後記錄故障。

清單 9. 檢測常見 Web 故障

 /** Call of send error in the Servlet API */
 public pointcut sendingErrorResponse(HttpServletResponse response,
       int sc) :
   target(response) && call(* sendError(..)) && args(sc, ..);  
 /** Track a failure after sending an error response */
 after(HttpServletResponse response, RequestContext requestContext,
     int sc) returning :
    sendingErrorResponse(response, sc) && inRequest (requestContext) {
   requestContext.setErrorContext(new Integer(sc));
 }
 /** Execute handle page exception in the JSP API */
 public pointcut handlePageException(PageContext pageContext,
      Throwable t) :
   call(* PageContext.handlePageException(*)) &&
   target(pageContext) && args(t);
 /** Track a failure when showing an error page */
 after(PageContext pageContext, RequestContext requestContext,
     Throwable t) returning :
  handlePageException(pageContext, t) && inRequest(requestContext) {
   requestContext.setErrorContext (t);
 }

擴展庫方面

有些時候,當我構建 Glassbox Inspector 時,需要讓庫方面更有可擴展性 。 在下面列出了一些可用的常見選項。其中一些選項我已經展示過了,它們都很有 用:

提供一個抽象切點以定義方面用在什麼地方(例如,一個 scope() 定義)。

提供一個空的切點,必須為要使用的某個建議覆蓋它(如在 清單 2 中的 AbstractOperationMonitor)。在許多情況下,少數的具體方面需要使用給定的 建議,因此有一個空的默認值更有用。像這樣的模板切點有一個空的默認實現與 模板方面有一個空的默認實現很相像。

利用標志性接口表明方面應用於哪些類型。因為接口是繼承的,所以這些類 型 的所有子類型都包括在內了。

利用 Java 5 注釋表明方面應用於哪些類型和/或方法。這種方法使您可以細 化對方面應用領域的控制,可以按方法定義,以及包括類而不包括它的子類。

提供運行時行為配置,有測試一種功能是否啟用的邏輯。例子請參閱 運行時 控制。

在應用程序中集成了這些擴展後,我就完成了對 Glassbox Inspector 監視 的 擴展(至少對於本文的目的 —— 本文結束的地方就是開放源碼項目開始的地方 !)。我現在有了一個將應用程序性能按照邏輯組織到一起的有用視圖,這樣就 可以通過 web 應用程序中的請求用標准 JMX 客戶機觀察總體性能統計和錯誤。 對於有疑問的地方,我還可以深挖數據庫或者 Web 服務的性能和可靠性。圖 1 顯示了在 Spring Petclinic、iBatis JPetstore 和 Duke 的 Bookstore Web 應 用程序例子中使用的 Glassbox Inspector 監視工具:

圖 1. 使用中的完整監視基礎架構的快照

錯誤處理

在這之後,我將重點放到如何更容易地部署擴展的 Glassbox Inspector 監 視 基礎架構上。邁向用於企業管理的基礎架構的第一步是擴展 Glassbox Inspector 來實現錯誤隔離。傳統上,開發人員盡可能保證像性能監視這樣的功能不會產生 影響被監視應用程序的錯誤。不過,監視代碼中未預料到的行為(如未預計到的 null 值、拋出異常的代理等)有時會在監視代碼自身中產生錯誤。在這些情況 下 ,隔離 錯誤的能力可提高監視實現的健壯性。系統初始化時出現的問題可能造 成 錯誤隔離方面不能緩沖的錯誤,但是這種問題通常是立即發生的,並容易在開發 時捕獲,使得它的風險降低了很多。

我的基本錯誤隔離策略是,捕獲並處理所有可能從建議執行連接點拋出的 Throwable,這樣它們就不會影響底層的應用程序。注意,建議執行切點匹配一 些 建議執行的連接點,因此這個方面將影響其他方面的行為。這種策略對於 before 和 after 建議都可以很好地工作,但是 around 建議實現錯誤隔離要復雜一點 兒 。如果在達到 proceed 語句之前拋出異常,那麼我會仍然繼續原來的連接點。 相 反,如果異常是在 proceed 語句處 拋出的,那麼我會讓異常通過而不會“隔離 ”它(即不吞掉它)。

因為在建議中沒有調用 proceed 的連接點,因此不能編寫像我需要的那樣完 全一般化的建議。不過,可以用 Glassbox Inspector 的結構提供它所使用的那 種 around 建議的錯誤隔離。在 Glassbox Inspector 中惟一的 around 建議總 是構造一個工人對象,然後對它調用 execute() 模板方法,這個方法調用 doExecute() 以完成原來的連接點。因此我將處理由監視器中 (AbstractRequestMonitor 或者它的任何子類)的 helper 方法或者由在 execute 或者 doExecute() 中的調用返回的所有異常。清單 10 顯示了我是如 何 擴展 Glassbox Inspector 以處理錯誤的:

清單 10. Glassbox Inspector 中的錯誤處理

public aspect ErrorHandling {
 public pointcut scope() :
    within(glassbox.inspector..*) && !within (ErrorHandling+);
 public pointcut inMonitor() :
   within (AbstractRequestMonitor+) || within(RequestContext+);
 public pointcut voidReturnAdviceExecution() :
   adviceexecution() &&
   if(((AdviceSignature) thisJoinPointStaticPart.getSignature()).
       getReturnType () == void.class);
 protected pointcut monitorHelperExec() :
    inMonitor() && execution(* *(..)) &&
   ! execution(* execute(..)) && !execution(* doExecute (..));

 protected pointcut monitorExecuteCall() :
    (inMonitor() && withincode(* execute(..)) ||
    withincode(* doExecute(..))) &&
   (call(* *(..)) && !call(* doExecute(..)) || call(new(..)));

 public pointcut handlingScope() :
    scope() &&
     voidReturnAdviceExecution() || monitorHelperExec() || monitorExecuteCall();
 /**
  * This advice ensures that errors in the monitoring code will not
  * poison the underlying application code.
  */
 Object around() : handlingScope() {
   try {
     return proceed();
   } catch (Throwable e) {
     handleError(e, thisJoinPointStaticPart);
      return null;
   }
 }
 public synchronized void handleError(Throwable t,
     StaticPart joinPointStaticPart) {
   // log 1 in 1000 but don't rethrow
   ...
 }
  /**
  * Count of errors: a weak map so we don't leak memory after apps
  * have been disposed of.
  */
 private Map/* <JoinPoint.StaticPart, MutableInteger> */errorCount =
    new WeakHashMap();

...
}

關於錯誤隔離層

從清單 10 中可以看到我首先定義了范圍的切點,以限定 ErrorHandling 中 的建議只應用於 Glassbox Inspector 中的代碼,而不應用到 ErrorHandling 方面本身。然後,我定義了 inMonitor() 切點以定義詞句上處於請求監視器中 的代碼(包括所有工人對象)。我定義了 voidReturnAdviceExecution() 切點 ,通過對建議執行的簽名使用一個基於 if 的測試以匹配建議執行。before 和 after 建議總是返回 void,而 Glassbox Inspector around 建議不會這樣做。 對於我的系統,這相當於匹配 before 和 after 建議而不是 around 建議的建 議執行。

下一步,我定義 monitorHelperExec() 以匹配監視器中 helper 方法的執行 (即所有不是 execute() 或者 doExecute() 的調用)。我定義了 monitorExecuteCalls() 為監視器中所有在 execute() 或者 doExecute() 方法 中的調用,而不是對 doExecute() 本身的調用。這些切點結合在 handlingScope() 中以定義我要處理異常的連接點。

最終的效果是,在所有 before 或者 after 建議和監視 around 建議處處理 異常以避免錯誤影響應用程序代碼,但是又不會吞掉應用程序異常。為了處理異 常,我捕獲所有由進程拋出的 Throwable,然後委派給 helper 方法以在第一次 遇到時記錄它們。然後建議返回,並且不拋出異常。Glassbox Inspector 還使 用 AspectJ 的 declare soft 格式向編譯器表明它在建議中處理了 checked 異 常。

總之,我可以創建一個非常有效的錯誤隔離層。在 Glassbox Inspector 中 增加這一功能所需要的修改開銷很小,從後面的性能測量中就可以看出來。

運行時控制

構建了一個有用的監視基礎架構後,現在我想在不重新啟動服務器的條件下 增加對所運行的監視的運行時控制。運行時控制基礎架構還讓 Glassbox Inspector 的用戶增加開銷更大的監視器以在系統出問題時捕獲更詳細的數據。 可以在運行時動態地啟用或者禁止整個監視,也可以通過增加一個 if 測試來檢 查每一個建議的切點的簡單標志,從而分別啟用或者禁止個別建議。為了提供控 制的常用框架,我使用清單 11 中的表達來啟用和禁止每一個方面的建議:

清單 11. 在運行時啟用和禁止建議

public aspect AbstractRequestMonitor {
...
protected pointcut scope() : if(true);
protected pointcut monitorEnabled() : isMonitorEnabled() && scope();
protected abstract pointcut isMonitorEnabled();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
protected boolean enabled = true;
}
public aspect JdbcStatementMonitor extends AbstractResourceMonitor {
...
/** Monitor performance for executing a JDBC statement. */
Object around(final Statement statement) :
statementExec(statement) && monitorEnabled() {
...
}
...
protected pointcut isMonitorEnabled() : if(aspectOf().isEnabled ());
}

很自然地,我使用 JMX 提供必要的運行時控制,如清單 12 所示:

清單 12. 啟用監視器的 JMX 管理

public aspect MonitorJmxManagement {
/**
* Management interface for monitors allows enabling and disabling
* at runtime.
*/
public interface RequestMonitorMBean extends ManagedBean {
public boolean isEnabled();
public void setEnabled(boolean enabled);
}
/**
* Make the {@link AbstractRequestMonitor} aspect implement
* {@link RequestMonitorMBean}, so all instances can be managed
*/
declare parents: AbstractRequestMonitor implements MonitorMBean;
public String AbstractRequestMonitor.getOperationName() {
return "control=monitor,type="+getClass().getName();
}
public MBeanInfoAssembler AbstractRequestMonitor.getAssembler() {
if (assembler == null) {
initAssembler();
}
return assembler;
}
...
}

MonitorJmxManagement 方面定義了監視器的管理接口,它只包括一個屬性 enabled。 通過 JMX 管理監視器所需要的其他支持來自在 第一部分 中討論的 JmxManagement 方面。我還將一些常用代碼從 StatsJmxManagement 中轉移到這個共享類中。圖 2 顯示了用 JMX 在運行時控制監視的例子:

圖 2. 在運行時控制監視

部署 Glassbox Inspector

到此為止,我已經可以為應用程序監視部署這個 Glassbox Inspector 了。 我讓它盡可能只應用到應用程序代碼上。不過,在許多情況下,將方面應用到庫 代碼是有利的。對於我工作的 Petclinic 和 JPetstore 示例應用程序來說, JDBC 訪問是由 Spring 和 iBatis 庫代碼分別處理的,在 Petclinic 中,代碼 控制器邏輯是由 Spring 庫處理的。因此讓監視方面影響應用程序所使用的一些 庫代碼是有好處的。有時,可以通過對來自應用程序代碼的調用進行建議,或者 在可以被建議的應用程序代碼中增加顯式鉤子(如繼承在庫類中定義的方法), 從而避免對庫代碼的執行進行建議。

我可以選擇兩種基本方法將監視織入到應用程序中:在編譯時或者在裝載時 。如果使用編譯時織入,那麼處理二進制 jar(例如,使用 ajc -inpath 命令 行工具進行織入)容易得多,不用從源代碼重新編譯庫。我在過去使用這種方法 ,它對應用程序啟動提供了最好的性能,並且只有很少的內存開銷。不過,它會 顯著增加編譯環境的復雜性。一個問題是要求 AspectJ 織入器能夠解析對包含 在被織入的 jar 中的第三方類的引用。對於 Spring,這意味著包括 Hibernate 、多種開放源碼緩沖管理器和 Servlet API 這些 JAR。

VM 中的 AOP 支持BEA JRockIt 最近提供了一種原型 Java VM,它支持方面 。這種集成帶來了 AOP 的高性能和後綁定(late-binding)支持,這是這兩個 方面最好的事情。我期待著這種技術的成熟和其他 Java VM 創造者的貢獻。

對於 AspectJ 5 的另一種可行的方法是裝載時織入。使用這種方法可以分別 編譯監視方面,並加入一個小的部署描述符以定義這個方面用在什麼地方。

裝載時織入

清單 13 展示了部署描述符 aop.xml 的一個例子,它描述了裝載時織入應用 在什麼地方。AspectJ 5 裝載時織入代理在系統內所有類 ClassLoader 都可訪 問的一個目錄或者 jar 中尋找所有 META-INF/aop.xml 資源。這些方面可以被 任何 Java ClassLoader 代理(如 Java 5 JVMTI -javaagent、JRockIt JVM 代 理或者甚至 WebSphere 或者 WebLogic ClassLoader 插件程序)自動裝載,以 將方面應用到系統中所有代碼或者部分代碼上。這使我可以對一個應用程序或者 一個服務器進行監視,而不用事先建立它,也不用改變編譯過程。

注意(例如)Spring 框架中引用不在類路徑中的類的代碼永遠也不裝載,因 此這種方法不需要增加額外的 jar。在 2005 年 11 月撰寫本文的時候, AspectJ 5 Milestone 4 已經發布,AspectJ 5 的最終版本預計在年底發布。裝 載時織入在保持大型系統中的模塊獨立性方面非常有用,它為方面提供了 Java 類裝載為對象提供的同樣的運行時解決方案。

清單 13. 裝載時織入配置

<aspectj>
<weaver>
<exclude within="org.springframework.jmx..*"/>
<!-- don't reweave -->
<exclude within="glassbox.inspector..*"/>
</weaver>
<aspects>
<aspect
name="glassbox.inspector.monitor.operation.AxisOperationMonitor&q uot;/>
<aspect
name="glassbox.inspector.monitor.operation.SpringMvcMonitor" />
<aspect
...
<aspect
name="glassbox.inspector.monitor.resource.JdbcStatementMonitor&qu ot;/>
<concrete-aspect
name="glassbox.inspector.monitor.operation.CustomMvcMonitor "
extends="glassbox.inspector.monitor.operation.TemplOperationMonit or">
<pointcut name="classControllerExecTarget"
expression="execution(* com.ibatis.struts.BaseBean..*(..))
&& cflow(execution(* com.ibatis.struts.BeanAction.execute (..)))"/>
</concrete-aspect>
</aspects>
</aspectj>

清單 3 中的 aop.xml 文件首先定義 AspectJ 織入器在什麼地方應該和不應 該應用方面。在默認情況下,它影響所有通過 ClassLoader 裝載的代碼。我排 除了系統的某些部分,在那些地方不需要裝載時織入以加快啟動時間。

AOP 配置文件然後列出要使用的所有方面。目前,這個方面清單必須手工維 護,這通常是容易出錯的地方。幸運的是,AspectJ 5 的最終版本計劃包含在編 譯方面時生成這些清單的工具。

最後注意,這個示例文件包括一個 XML 定義的方面 CustomMVCMonitor。這 個方面擴展了一個抽象方面以監視一個特定於應用程序的框架。基本上,它只定 義了一個針對要監視的操作的切點。這表明了如何在不編譯任何代碼的情況下擴 展 Glassbox Inspector 以監視自定義代碼。還可以通過將監視方面從 aop.xml 文件中刪除而不讓它們運行。如果以這種方式取消它們的部署,可以降低開銷, 但是不能在不重新啟動應用程序(或者服務器)的條件下重新啟用它們。這種技 術還可以用於限制監視。例如,可以定義一個更狹窄的切點以監視在一個 XML 定義的方面中的 Struts 操作,並排除標准的 StrutsMonitor。

運行帶裝載時織入的 Tomcat 5.5

我將展示在 Tomcat 5.5 應用服務器中部署 Glassbox Inspector。這個服務 器默認實現了 Java 5,因此我使用 Java 5 的 -javaagent 啟動參數調用 AspectJ 5 裝載時織入。(請參閱 參考資料 以了解關於對更早版本的 Java 語 言使用 AspectJ 裝載時織入的知識。)要使用這個功能,只需要對啟動命令增 加一個新的 java 選項標志。這通常是通過編譯一個 shell 腳本完成的。例如 ,可以在 Windows 計算機中的 setclasspath.bat 的開始處增加以下一行:

set JAVA_OPTS=-javaagent:%CATALINA_HOME% \common\lib\aspectjweaver.jar -Xmx256m

可以對於 Linux 或者其他 Unix 操作系統的 setclasspath.sh 腳本做類似 的修改。

這一設置就可以在 Tomcat VM 中允許裝載時織入。要監視 Web 應用程序, 可以像通常那樣編譯它,並在以後在 WEB-INF/lib 目錄中增加 glassboxInspector.jar 文件及其依賴文件。AspectJ 織入器然後尋找我部署的 aop.xml 文件,並保證它定義的方面在應用程序裝載時被織入。另一種方法是, 將 glassboxInspector.jar 和文件加到 Tomcat 的 shared/lib 文件夾中。這 會增加對服務器中所有 web 應用程序的監視,而不需要為每一個應用程序增加 這個 jar。這種解決方案類似於為應用服務器中的一個應用程序或者所有應用程 序增加任何其他庫功能。我甚至可以將這個監視器放到系統類路徑中或者 Tomcat 的 common/lib 文件夾中。這樣就可以監視 Tomcat 的內部,不過我看 不出這有什麼必要,而且這樣做會增加啟動時間。

我選擇一次性地在整個服務器上部署監視。一般來說,應用服務器是作為整 體管理的,最好對服務器上的所有應用程序有一致性的管理和監視能力。

性能和內存使用

裝載時織入的簡單性的代價是啟動應用程序需要更多的時間。我一直與 Alexandre Vasseur 和 Matthew Webster 進行努力,盡量在 AspectJ 5 發布之 前優化裝載時織入的性能。開銷已經不算大了,我們找到了一些可以加入的優化 。我預計裝載時織入性能在下一年仍然會繼續進一步優化。同樣,這些測試是對 Glassbox Inspector 的 alpha 版進行的。我預計在調優 Glassbox Inspector 和 AspectJ 裝載時織入後,開銷會顯著降低。我計劃優化 Glassbox Inspector 以使用 java.util.concurrent 庫,使用的方面可以根據系統運 行的是哪種 Java VM,使用多個實現中最有效的一種。

我對 Windows XP 工作站上 2005 年 10 月開發版本的 Aspect J(1.5.0 M4 之後)的服務器啟動時間和每個請求的平均時間做了一些簡單的測量。這個系統 是 Inspector 的開發版本(alpha 1 之後)。在我的測試中,在 JRockIt 1.5.0_03 Java VM 上運行 Tomcat 5.5.9,並自動啟動 iBatis 1.3 JPetstore 、Spring 1.2.1 Petclinic 和 Duke 的 Bookstore 應用程序。

當系統啟動後,端到端的響應時間開銷很小。50 位用戶同時運行在同一台計 算機中時,平均服務器響應時間增加了 10 毫秒。不過,帶裝載時織入的啟動用 了約 15 秒,包括織入時間。對應地,不帶織入的啟動用了大約 5 秒。最後, 使用裝載時織入顯著增加了內存開銷:有織入與沒織入時相比,Java 進程使用 的內存增加了 100%。

表 1 顯示了當啟用 Glassbox Inspector 並帶裝載時織入時、部署但禁用了 裝載時織入時以及沒有部署時,每個請求的開銷、淨啟動時間和內存使用:

表 1. 帶裝載時織入時的開銷 監視 平均端到端
響應時間(毫秒) 服務器啟動
時間(毫秒) 啟動進程
內存使用(MB) 啟用 20 15 135 禁用(在運行時) 10 130 未部署 10 5 65

結束語

在這篇由兩部分組成的文章中,我向您展示了如何用 AspectJ 處理復雜的橫 切問題(在這裡是應用程序監視),並逐步構造解決方案。即使您在下一個監視 解決方案中不選擇使用面向方面的方法,這裡所講的許多內容和技巧也會證明是 很有價值的。在我的其他 AspectJ 項目中發現它們顯然是很有用的。

就是說,我希望本文提供了使用面向方面的代碼進行監視的好例子。與傳統 的分散而且糾纏的測試代碼相比,我編寫的方面非常容易擴展和改編,它們提供 了獲得關於生產應用程序的數據的更好方法。如果沒有方面,那麼實現在這裡描 述的復雜功能已經非常困難,更不用說隨著對生產應用程序的了解加深而改寫它 了。

對 Glassbox Inspector 監視基礎架構的開發和分析不會隨著本文而結束。 這個項目正在尋求多個領域的貢獻,如:

捕獲關於所發生情況的更多上下文(例如,堆棧跟蹤和參數),特別是像故 障或者不正常響應時間這樣的反常結果。

監視更多操作、資源,如 JMS 和 EJB,並擴展 Inspector 對監視 XML 文檔 處理的基本支持。

處理分布式監視,以便跨集群應用程序跟蹤信息,並跨分布式調用對信息進 行關聯。

利用 Java 5 管理信息,如 CPU 時間或者特定於線程的統計。

使用應用服務器 JMX 統計,如線程池。

捕獲歷史和趨勢,帶持久化存儲和報告。

使用 JMX 提供報警,並提供統計匯總。累積 JMX 統計中關於嵌套的監視器 的關鍵信息會很有用。

監視頂級資源信息(例如,調用服務或者連接到數據庫所花費的總時間)。 這可以提供更好的匯總數據。

提供不同程度的統計匯總(例如,一個請求所花時間的柱狀圖)。

適應性地發現要跟蹤的相關參數(例如,對於未知的數據查詢或者 Servlet 請求)。

對於高層數據庫和服務訪問框架提供資源監視(如 Hibernate、TopLink、 EJB 3 Persistence Managers、JAX-WS 等)。

允許取樣以改變(跨整個請求)所捕獲的數據量。

監視對沒有綁定到 Servlet 的 Web 應用程序的請求錯誤,如 404 錯誤。這 是對 Servlet 過濾器進行建議的好例子,包括增加一個空的 Servlet 過濾器以 匹配對一個應用程序的所有請求。

監視業務事件,如客戶購買或者放棄購物車。

現在該您了。我鼓勵您下載 Glassbox Inspector 並試驗它。如果您能為這個項目出力,不管是提供反饋意 見、開發其他的監視器、沿上面建議的方向擴展系統,還是為該項目建立一個新 的方向,我都會很高興。Glassbox Inspector 的完整源代碼請參閱 下載。請參 閱 參考資料 以了解更多關於這裡討論的主題的內容。

下載:http://www.ibm.com/developerworks/cn/java/j-aopwork12/

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