使用AOP的方式監測方法執行耗時

  在一些對系統中,每每可能須要對一些核心業務作相應的監測。如:記錄調用參數,返回值,方法執行耗時等等。若是直接在方法的先後加入代碼,以下: app

 1  public int F(int a, string s)
 2         {
 3             var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff");
 4             var sw = Stopwatch.StartNew();
 5 
 6             int ret = 1;
 7 
 8             long second = sw.ElapsedMilliseconds;
 9             var end = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff");
10             log.Write(now, end, second, a, s, ret);
11         }

這樣有如下幾點問題:ide

  1.將業務邏輯和方法執行監測混在一塊兒。性能

  2.會有大量重複代碼,哪怕你作了一層封裝,也依然不夠優雅。spa

因爲有了以上的問題,咱們會思考,若是能夠將方法的調用過程抽象出來,在方法開始調用和方法調用結束時,植入代碼,這樣就能夠很好的解決咱們的問題。pwa

  .NET中,咱們能夠經過RealProxy來實現咱們想要的功能。RealProxy(https://msdn.microsoft.com/zh-cn/library/vstudio/system.runtime.remoting.proxies.proxyattribute(v=vs.100).aspx)MSDN的定義。在Invoke方法中,能夠攔截方法的執行,那麼咱們就能夠在方法的執行先後作一些事情啦。代理

  那麼怎麼更優雅的應用在方法上呢,咱們能夠用ProxyAttribute使用Attribute的方式。能夠在對象初始化的時候(new關鍵字)建立相應的代理類。具體代碼以下:code

    /// <summary>
    /// 標記要性能監測的類
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class MonitorServiceAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            var instance = base.CreateInstance(serverType);
            var proxy = new InjectProxy(serverType, instance).GetTransparentProxy();
            return proxy as MarshalByRefObject;
        }

    }

InjectProxy代碼以下:server

 public override IMessage Invoke(IMessage msg)
        {
            var call = (IMethodCallMessage) msg;
            var ctr = call as IConstructionCallMessage;

            IMethodReturnMessage back;
            if (ctr != null)
            {
                RealProxy defaultProxy = RemotingServices.GetRealProxy(_instance);
                defaultProxy.InitializeServerObject(ctr);
                back = EnterpriseServicesHelper.CreateConstructionReturnMessage(ctr,
                    (MarshalByRefObject) GetTransparentProxy());
            }
            else
            {
                var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff");
                var sw = Stopwatch.StartNew();
                back = RemotingServices.ExecuteMessage(_instance, call);
                long second = sw.ElapsedMilliseconds;
                var attr = MethodAttributeCache.GetAttribute<MonitorMethodAttribute>(_instance.GetType().TypeHandle,
                    call.MethodBase);

                if (attr != null)
                {
                    attr.Value = second.ToString();
                    attr.ExecuteTime = now;
                    Queue.Enqueue(Mapper(attr));
                }
            }
            return back;
        }

我在這裏只記錄了方法執行耗時,能夠從call裏拿到參數,從back裏獲取到返回值。對象

具體調用地方以下,注意類須要繼承ContextBoundObject:blog

  internal class Program
    {
        private static void Main(string[] args)
        {
            var t = new Test();
            t.F(1,"aaa");
        }
    }

    [MonitorService]
    internal class Test : ContextBoundObject
    {
        [MonitorMethod("A")]
        public int F(int a,string s)
        {
            return 333;
        }

    }
相關文章
相關標籤/搜索