2014-09-30 70 views
1

我想要做的是: 通過JMX公開呼叫統計信息(基本上調用計數和平均呼叫時間)。 問題是我想用AspectJ支持來做到這一點(爲我的實現類自動計算它)。使用JMX編程式公開呼叫統計信息

我創造了什麼是JmxBean:

public class JmxStatistics implements Serializable { 

private final String name; 
private long errorCount; 
private long errorCallTime; 
private long successCount; 
private long successCallTime; 

public JmxStatistics(String name) { 
    this.name = name; 
} 

public synchronized long getCallCount() { 
    return errorCount + successCount; 
} 

public synchronized long getAvgCallTimeInMillisecs() { 
    return (errorCallTime + successCallTime)/getCallCount(); 
} 

public synchronized long getAvgSuccessfulCallTimeInMillisecs() { 
    return (errorCallTime + successCallTime)/successCount; 
} 

public synchronized long getAvgFailedCallTimeInMillisecs() { 
    return (errorCallTime + successCallTime)/errorCount; 
} 

public synchronized void increaseErrorCallTime(long sumCallTime) { 
    this.errorCallTime += sumCallTime; 
} 

public synchronized void increaseSuccessCallTime(long sumCallTime) { 
    this.successCallTime += sumCallTime; 
} 

public synchronized long getErrorCount() { 
    return errorCount; 
} 

public synchronized void incrementErrorCount() { 
    this.errorCount++; 
} 

public synchronized long getSuccessCount() { 
    return successCount; 
} 

public synchronized void incrementSuccessCount() { 
    this.successCount++; 
} 

@Override 
public String toString() { 
    return name + "{" + "callCount=" + getCallCount() 
      + " (s: " + successCount + ",e:" + errorCount + "); " 
      + "avgCallTime=" + getAvgCallTimeInMillisecs() + "ms " 
      + "(" + getAvgSuccessfulCallTimeInMillisecs() + "ms;e:" + getAvgFailedCallTimeInMillisecs() + "ms)" + "}"; 
} 

的AOP包點:

public Object wrap(ProceedingJoinPoint joinPoint) throws Throwable { 
    JmxStatistics jmxs = store.getStatisticsBean(joinPoint); 
    long start = System.currentTimeMillis(); 
    Object result; 
    try { 
     result = joinPoint.proceed(); 
    } catch (Throwable t) { 
     long runtime = System.currentTimeMillis() - start; 
     jmxs.incrementErrorCount(); 
     jmxs.increaseErrorCallTime(runtime); 
     throw t; 
    } 
    long runtime = System.currentTimeMillis() - start; 
    jmxs.incrementSuccessCount(); 
    jmxs.increaseSuccessCallTime(runtime); 
    return result; 
} 

而對於公開所有(編程創建)JMX豆商店:

public class JmxStatisticsStore { 

private final HashMap<String, JmxStatistics> jmxBeans = new HashMap<>(); 

public HashMap<String, JmxStatistics> getJmxBeans() { 
    return jmxBeans; 
} 

JmxStatistics getStatisticsBean(ProceedingJoinPoint joinPoint) { 
    String id = joinPoint.getTarget().getClass().toString() + "." + joinPoint.getSignature().getName(); 
    if (!jmxBeans.containsKey(id)) { 
     synchronized (jmxBeans) { 
      if (!jmxBeans.containsKey(id)) { 
       JmxStatistics jmxStatistics = new JmxStatistics(id); 
       jmxBeans.put(id, jmxStatistics); 
      } 
     } 
    } 
    return jmxBeans.get(id); 
} 
} 

我想爲此使用Spring AOP和MBeanExporter。我的配置如下:

<bean id="jmxStatisticsStore" class="package.JmxStatisticsStore"/> 
<bean id="jmxAspect" class="package.AspectJJmxCalculator" /> 
<aop:config>   
    <aop:aspect id="jmxAspectId" ref="jmxAspect"> 

     <aop:pointcut id="pointCutAround" 
         expression="execution(* jmx.tester..*.*(..))" /> 

     <aop:around method="wrap" pointcut-ref="pointCutAround" /> 
    </aop:aspect> 
</aop:config> 
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> 
    <property name="beans"> 
     <map> 
      <entry key="bean:name=callStatistics" value-ref="jmxStatisticsStore"/> 
     </map> 
    </property> 
</bean> 

目前,我嘗試在Tomcat的運行它(jmx.tester包有我在web應用程序使用的Spring bean)。該webapp運行正常,並與JConsole我可以連接到服務器。在服務器上,在bean下,我可以看到帶有HashMap屬性的callStatistics bean。

直到我沒有打電話,散列表是空的(這很好)(在「Value」框中顯示{}) 在我撥打電話後(因此散列表不再爲空),價值變得不可用,如果我嘗試調用通過JMX的getJmxBeans方法,我得到以下異常:

Problem invoking getJmxBeans: java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
java.lang.ClassNotFoundException: package.JmxStatistics (no security manager: RMI class loader disabled) 

編輯:我想看到的是,對於不同的ID來電顯示爲單獨的JMX屬性。

有沒有辦法做我想完成的事情? Thx

回答

0

託管解決這個問題。 修改了JmxStatisticsStore:

@Autowired 
MBeanExporter exporter; 

private static final Logger LOG = LogManager.getLogger(); 
private final HashMap<String, Object> jmxBeans = new HashMap(); 

JmxData getStatisticsBean(ProceedingJoinPoint joinPoint) { 
    String id = "bean:name=" + joinPoint.getTarget().getClass().toString() + "." + joinPoint.getSignature().getName(); 
    if (!jmxBeans.containsKey(id)) { 
     synchronized (jmxBeans) { 
      if (!jmxBeans.containsKey(id)) { 
       JmxData jmxStatistics = new JmxData(id); 
       jmxBeans.put(id, jmxStatistics); 
       try { 
        exporter.registerManagedResource(jmxStatistics, new ObjectName(id)); 
       } catch (MalformedObjectNameException ex) { 
        LOG.warn("Error while registering " + id, ex); 
       } 
      } 
     } 
    } 
    return (JmxData) jmxBeans.get(id); 
} 

這種辦法,可以在運行時創建新的MBean

1

我建議你Metrics library

它有Spring integration和通過JMX報告。

使用這個庫到Spring很容易使用註釋。這是一個例子;

公共類Foo {

@Timed 
public void bar() { /* … */ } 

public void baz() { 
    this.bar(); // doesn't pass through the proxy 

    // fix: reengineer 
    // workaround: enable `expose-proxy` and change to: 
    ((Foo) AopContext.currentProxy()).bar(); // hideous, but it works 
} 

}

+0

嗯......稍後會檢查這一點,如果它是比我們現在更好的解決方案 – SzaboAdam 2014-09-30 13:53:59