在大型系統中,我們經常需要監視我們系統執行的性能狀況,當出現性能問題時,我們要 能夠迅速地找到瓶頸在什麼地方。在程序的層面上來說,就是看哪個方法執行所消耗的時間 很長。
使用動態代理可以非常方便的記錄方法執行的時間,比如,下面的截圖,就是 ESBasic.Emit.Aop.Interceptors.MethodTimeInterceptor截獲器記錄的片段:
2009-4-17 18:50:12:TY.Web.AgentInterface.IGameRecordDetailBL.GetPaginationData方法耗 時:390ms
2009-4-17 18:50:16:TY.Web.MemberInterface.IMemberBL.GetOne方法耗時 :106ms
2009-4-17 18:50:23:TY.Web.MemberInterface.IMemberBL.GetOne方法耗 時:105ms
2009-4-17 18:50:23:TY.Web.MemberInterface.IMemberBL.GetOne方法耗時 :105ms
2009-4-17 18:50:24:TY.Web.AgentInterface.IGameRecordDetailBL.GetPaginationData方法耗 時:386ms
2009-4-17 18:50:30:TY.Web.AgentInterface.IGameRecordDetailBL.GetPaginationData方法耗 時:387ms
2009-4-17 18:50:35:TY.Web.AgentInterface.IGameRecordDetailBL.GetPaginationData方法耗 時:377ms
2009-4-17 18:50:43:TY.Web.MemberInterface.IMemberBL.GetOne方法耗時 :105ms
2009-4-17 18:51:14:TY.Web.MemberInterface.IGameBenefitBL.GetMemberBenefitData方法耗 時:714ms
在我們常見的三層架構中,我們可能需要記錄UI層調用BL層的每個方法所執行的時間,那 我們可以這樣做。假設BL提供給UI訪問的接口為IBL(可能有多個,如IBL1,IBL2, IBL3,......),BL通過Remoting的方式發布它的服務。
//BL層給UI層調用的接口
public interface IBL
{
}
//BL的實現
public class BLObject : IBL
{
}
那麼,我們讓BL不直接發布實現了IBL接口的對象(BLObject),而是發布攔截了這個對 象的動態代理,就像這樣:
IMethodTimeLogger logger = new MethodTimeLogger(new FileAgileLogger("TimeLogger.txt") ,100);
IBL blObject = ...; //BLObject 的實例
IBL blObjectProxy = DynamicProxyFactory.CreateAopProxy<IBL>(blObject, null, new MethodTimeInterceptor(logger));
blObjectProxy即是攔截了blObject調用的動態代理,它和BLObject 一樣實現了IBL接口 ,並且將所有的調用轉發給blObject,而且記錄了blObject每個方法執行的時間。我們可以 將blObjectProxy對象發布為Remoting,這樣就可以記錄下BL每個方法的執行時間了。
在上面的例子中,IMethodTimeLogger 接口是用於記錄時間的記錄器接口,我們可以將 時間記錄到日志文件,當然也可以記錄到數據庫或其他地方,你只要實現IMethodTimeLogger 接口就可以了。這裡我們將時間記錄到TimeLogger.txt文件,注意,MethodTimeLogger 的第二個參數100,表示只記錄那些執行時間超過100ms的方法調用,因為如果要記錄所有的 方法調用的話,在大型系統中日志可能會在很短的時間內就變得非常的龐大,這樣反而不利 於日志查看。
再回到上面的日志片段,我們看到,日志記錄了BL接口中具體方法的調用時間,執行所消 耗的時長,這樣我們就可以很方便的找到性能的瓶頸是在那個方法的調用上產生的了。
上述例子中所用到的類都位於ESBasic.dll中,你可以下載進行試驗。ESBasic中的動態代 理技術是基於Emit實現的,有興趣的話你可以使用Reflector來查看其實現原理。
本文附件