基於Spring AOP實現對外介面的耗時監控

huidaoli發表於2013-08-01

AOP是Spring的核心,Spring不但自身對多種框架的整合是基於AOP,並且以非常方便的形式暴露給普通使用者。以前用AOP不多,主要是因為它以橫截面的方式插入到主流程中,擔心導致主流程程式碼不夠清晰,定位問題不夠方便,而在計費二期的專案裡需要一個很適合用AOP來做的功能,就是要把對外介面和所呼叫的外部介面的耗時時間給記錄下來,這個需求主要來自於計費一期的聯調,常常發生系統間互動不夠順暢的情況,這就需要看每個介面呼叫時間來判定是誰的問題。

計費中心是整個後臺系統的中間環節,與其他系統互動很多,這樣的介面也很多,如果在每個介面的呼叫前後加時間記錄比較繁瑣,也影響主流程程式碼的美觀,因此比較優雅的方式是用AOP,在不侵入原有程式碼的情況下,加上對介面呼叫的監控,並且可以在不需要的時候很容易移除。今天嘗試了一下,感覺還挺好用,下面講述一下實施步驟:

1)引入包依賴

本專案基於maven構建,因此加上包依賴比較方便,我需要的AOP依賴庫有以下三個:

[xhtml] view plaincopy
  1. <dependency>  
  2.   <groupId>org.springframework</groupId>  
  3.   <artifactId>spring-aop</artifactId>  
  4.   <version>2.5.6</version>  
  5. </dependency>  
  6. <dependency>  
  7.   <groupId>org.aspectj</groupId>  
  8.   <artifactId>aspectjweaver</artifactId>  
  9.   <version>1.6.1</version>  
  10. </dependency>  
  11. <dependency>  
  12.   <groupId>org.aspectj</groupId>  
  13.   <artifactId>aspectjrt</artifactId>  
  14.   <version>1.6.1</version>  
  15. </dependency>   

 

2)加上AOP的Spring配置檔案

billing-spring-aop.xml:

[xhtml] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>     
  2. <beans xmlns="http://www.springframework.org/schema/beans"    
  3.  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  4.  xmlns:aop="http://www.springframework.org/schema/aop"    
  5.  xmlns:tx="http://www.springframework.org/schema/tx"    
  6.  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd    
  7.  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd    
  8.  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">    
  9.    
  10.  <bean id="openApiLogAspect" class="com.alibaba.itbu.billing.framework.aop.OpenApiLogAspect">  
  11.  </bean>    
  12.    
  13.  <aop:config>    
  14.   <!-- 配置aspect切面類 -->    
  15.   <aop:aspect ref="openApiLogAspect">    
  16.    <!-- 配置pointcut,即切入點,對哪些類的哪些方法起到AOP的作用 -->    
  17.    <aop:pointcut id="EsbPriceService"  
  18.     expression="execution(* org.esb.biz.product.EsbPriceService.*(..))" />    
  19.    <aop:pointcut id="EsbProductService"  
  20.     expression="execution(* org.esb.biz.product.EsbProductService.*(..))" />    
  21.    <aop:pointcut id="IAuthorizeControllerService"  
  22.     expression="execution(* com.alibaba.bss.pc2.server.remoting.IAuthorizeControllerService.*(..))" />      
  23.    <aop:pointcut id="IOpenApiOrderItemService"  
  24.     expression="execution(* com.alibaba.itbu.billing.api.collect.IOpenApiOrderItemService.*(..))" />   
  25.    <aop:pointcut id="IOpenApiBillingCollectService"  
  26.     expression="execution(* com.alibaba.itbu.billing.api.collect.IOpenApiBillingCollectService.*(..))" />   
  27.    <aop:pointcut id="IOpenApiInvoiceService"  
  28.     expression="execution(* com.alibaba.itbu.billing.api.invoice.IOpenApiInvoiceService.*(..))" />   
  29.    <aop:pointcut id="IOpenApiChargeProductInfoService"  
  30.     expression="execution(* com.alibaba.itbu.billing.api.collect.IOpenApiChargeProductInfoService.*(..))" />       
  31.    <!-- 配置advice,這裡採用在業務方法執行前後進行攔截 -->    
  32.    <aop:around method="logExecuteTime" pointcut-ref="EsbPriceService" />  
  33.    <aop:around method="logExecuteTime" pointcut-ref="EsbProductService" />  
  34.    <aop:around method="logExecuteTime" pointcut-ref="IAuthorizeControllerService" />  
  35.    <aop:around method="logExecuteTime" pointcut-ref="IOpenApiOrderItemService" />  
  36.    <aop:around method="logExecuteTime" pointcut-ref="IOpenApiBillingCollectService" />  
  37.    <aop:around method="logExecuteTime" pointcut-ref="IOpenApiInvoiceService" />  
  38.    <aop:around method="logExecuteTime" pointcut-ref="IOpenApiChargeProductInfoService" />  
  39.   </aop:aspect>               
  40.  </aop:config>    
  41.   
  42. </beans>   

 

我是基於配置完成AOP接入,這樣做的好處是不需要對原有主流程程式碼有任何浸入,並且也比較容易移除本AOP的攔截,這段程式碼主要就是配置aspect、pointcut和advice

3)編寫監控耗時的advice

OpenApiLogAspect:

  1. public class OpenApiLogAspect {  
  2.  private static LoggerService logger = LoggerFactory.getLogger(OpenApiLogAspect.class);  
  3.  public Object logExecuteTime(ProceedingJoinPoint joinPoint) throws Throwable{  
  4.   Date start = new Date();  
  5.   try{  
  6.    return joinPoint.proceed(joinPoint.getArgs());  
  7.   }catch(Exception err){  
  8.    throw err;  
  9.   }finally{  
  10.    Date end = new Date();  
  11.    logger.info("OpenApiExecuteTime:"+joinPoint.getSignature().getName()+" takes "+(end.getTime()-start.getTime())+"ms");  
  12.   }  
  13.  }  
  14. }   

 

此段程式碼就是基於around的方式來攔截介面呼叫,在實際呼叫的前後加上時間記錄,並最後在日誌裡列印出時間差。其中joinPoint.proceed(joinPoint.getArgs());是對實際介面的呼叫。

4)使監控可以配置化

此功能只會在除錯階段使用,並不需要在生產環境中執行,因此需要可以配置是否監控介面。實施這個配置化很簡單,只需要通過配置決定是否把aop spring的配置檔案加入到容器裡就可以了,因此在總容器applicationContext.xml.vm里加上如下程式碼:

#if(${monitor_openapi_showTime}=="true")  <import resource="classpath*:bean/billing-spring-aop.xml" />  #end

在編譯打包過程中會根據變數monitor_openapi_showTime來決定是否把billing-spring-aop.xml引入進來

5)執行效果

在監控開啟的情況下,若發生介面呼叫,能從日誌裡看到如下記錄:

2010-01-08 18:30:13,197 [OpenApiLogAspect.java:20] [com.alibaba.itbu.billing.framework.aop.OpenApiLogAspect] INFO  com.alibaba.itbu.billing.framework.aop.OpenApiLogAspect :: OpenApiExecuteTime:installOrderItem takes 71ms 2010-01-08 18:30:27,188 [OpenApiLogAspect.java:20] [com.alibaba.itbu.billing.framework.aop.OpenApiLogAspect] INFO  com.alibaba.itbu.billing.framework.aop.OpenApiLogAspect :: OpenApiExecuteTime:installOrderItem takes 0ms 2010-01-08 18:30:37,838 [OpenApiLogAspect.java:20] [com.alibaba.itbu.billing.framework.aop.OpenApiLogAspect] INFO  com.alibaba.itbu.billing.framework.aop.OpenApiLogAspect :: OpenApiExecuteTime:installOrderItem takes 1ms

相關文章