使用AOP的方式监测方法执行耗时

  在一些对系统中,往往可能需要对一些核心业务做相应的监测。如:记录调用参数,返回值,方法执行耗时等等。如果直接在方法的前后加入代码,如下: 

 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         }

这样有以下几点问题:

  1.将业务逻辑和方法执行监测混在一起。

  2.会有大量重复代码,哪怕你做了一层封装,也依然不够优雅。

由于有了以上的问题,我们会思考,如果可以将方法的调用过程抽象出来,在方法开始调用和方法调用结束时,植入代码,这样就可以很好的解决我们的问题。

  .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关键字)创建相应的代理类。具体代码如下:

    /// <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代码如下:

 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:

  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;
        }

    }

 

posted @ 2015-03-25 12:05  gogo_baby  阅读(939)  评论(0编辑  收藏  举报