使用Spring AOP進行性能監控

jopen 10年前發布 | 16K 次閱讀 Spring JEE框架 Spring AOP

如果你正在使用Spring管理/訪問資源(Dao/Service),那么你可能也需要添加一些基礎的性能監控。在Spring AOP的幫助下這將變成一個簡單的任務,不需要任何現有代碼的變化,只是一些簡單的配置。

第一步,你首先的將spring-aop、aspectj和cglib庫導入,如果你使用maven管理你的項目依賴的話,很簡單加上如下依賴關系就可以了。
 

01     <dependency>
02         <groupId>org.aspectj</groupId>
03         <artifactId>aspectjweaver</artifactId>
04         <version>1.5.4</version>
05     </dependency>
06     <dependency>
07         <groupId>cglib</groupId>
08         <artifactId>cglib-nodep</artifactId>
09         <version>2.2</version>
10     </dependency>
11     <dependency>
12       <groupId>org.springframework</groupId>
13       <artifactId>spring-aop</artifactId>
14       <version>2.5.6</version>
15     </dependency>


接下來,指明你需要監視的內容,并把AOP配好。通常,僅僅需要在現有的SpringXML配置文件中增加一個橫切點。這個配置將會將位于包"com.mycompany.services"下的所有方法的響應時間記錄下來。注:這些類必須使用Spring context初始化,否則AOP將不會被執行。
 

1     <bean id="performanceMonitor"
2               class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor" />
3       
4     <aop:config>
5         <aop:pointcut id="allServiceMethods" expression="execution(* com.mycompany.services.*.*(..))"/>
6         <aop:advisor pointcut-ref="allServiceMethods" advice-ref="performanceMonitor" order="2"/>
7     </aop:config>


接下來,需要配置好日志系統,例如log4j。
 

1     <logger name="org.springframework.aop.interceptor.PerformanceMonitorInterceptor" additivity="false">
2         <level value="TRACE"/>
3         <appender-ref ref="STDOUT"/>
4     </logger>


ok了,現在我們運行一下程序你會發現下面的日志輸出:
 

1     TRACE PerformanceMonitorInterceptor  - StopWatch 'PerfTestService.processRequest': running time (millis) = 1322
2     TRACE PerformanceMonitorInterceptor  - StopWatch 'PerfTestService.processRequest': running time (millis) = 98
3     TRACE PerformanceMonitorInterceptor  - StopWatch 'PerfTestService.processRequest': running time (millis) = 1764


這些是大量的一些原始數據,但不幸的是這些東西對我們幾乎沒用,每一個方法調用都會有記錄,而且缺乏一些其他信息。所以,除非你打算寫一些日志分析程序、或者使用第三方軟件,否則的話,我想你應該在日志被記錄前做出一些處理。

一個簡單的辦法就是在這之間寫一個簡單的攔截器類來替代Spring給我們提供的默認的類(PerformanceMonitorInterceptor)。下面的一個例子,這個例子提供了一些有用的信息(最后一個、平均、最大的響應時間),另外當一個方法的響應時間超出指定的時間后給出警告。

默認的,每當十個方法調用的時候,做一次記錄,在任何方法響應時間超過1000ms的時候給出警告。

01     public class PerfInterceptor implements MethodInterceptor {
02       
03          Logger logger = LoggerFactory.getLogger(PerfInterceptor.class.getName());
04         private static ConcurrentHashMap<String, MethodStats> methodStats = new ConcurrentHashMap<String, MethodStats>();
05         private static long statLogFrequency = 10;
06         private static long methodWarningThreshold = 1000;
07          
08         public Object invoke(MethodInvocation method) throws Throwable {
09             long start = System.currentTimeMillis();
10             try {
11                 return method.proceed();
12             }
13             finally {
14                 updateStats(method.getMethod().getName(),(System.currentTimeMillis() - start));
15             }
16         }
17       
18         private void updateStats(String methodName, long elapsedTime) {
19             MethodStats stats = methodStats.get(methodName);
20             if(stats == null) {
21                 stats = new MethodStats(methodName);
22                 methodStats.put(methodName,stats);
23             }
24             stats.count++;
25             stats.totalTime += elapsedTime;
26             if(elapsedTime > stats.maxTime) {
27                 stats.maxTime = elapsedTime;
28             }
29              
30             if(elapsedTime > methodWarningThreshold) {
31                 logger.warn("method warning: " + methodName + "(), cnt = " + stats.count + ", lastTime = " + elapsedTime + ", maxTime = " + stats.maxTime);
32             }
33              
34             if(stats.count % statLogFrequency == 0) {
35                 long avgTime = stats.totalTime / stats.count;
36                 long runningAvg = (stats.totalTime-stats.lastTotalTime) / statLogFrequency;
37                 logger.debug("method: " + methodName + "(), cnt = " + stats.count + ", lastTime = " + elapsedTime + ", avgTime = " + avgTime + ", runningAvg = " + runningAvg + ", maxTime = " + stats.maxTime);
38                  
39                 //reset the last total time
40                 stats.lastTotalTime = stats.totalTime;   
41             }
42         }
43          
44         class MethodStats {
45             public String methodName;
46             public long count;
47             public long totalTime;
48             public long lastTotalTime;
49             public long maxTime;
50              
51             public MethodStats(String methodName) {
52                 this.methodName = methodName;
53             }
54         }
55     }


現在,你只需要將你的Spring配置文件中做相關修改,將這個類應用進去,再運行程序,你將會看到如下的統計信息。

 
1     WARN  PerfInterceptor - method warning: processRequest(), cnt = 10, lastTime = 1072, maxTime = 1937
2     TRACE PerfInterceptor - method: processRequest(), cnt = 10, lastTime = 1072, avgTime = 1243, runningAvg = 1243, maxTime = 1937
3     WARN  PerfInterceptor - method warning: processRequest(), cnt = 20, lastTime = 1466, maxTime = 1937
4     TRACE PerfInterceptor - method: processRequest(), cnt = 20, lastTime = 1466, avgTime = 1067, runningAvg = 892, maxTime = 1937


正如你看到的一樣,這些統計數據可以在不修改任何現有的Java代碼的情況下,提供有關class/method性能的有價值的反饋,而根據這個日志,你可以很輕松的找出程序中的瓶頸。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!