首页 > 其他分享 >.NET静态代码织入——肉夹馍(Rougamo) 发布1.2.0

.NET静态代码织入——肉夹馍(Rougamo) 发布1.2.0

时间:2022-09-19 03:22:34浏览次数:83  
标签:Task Rougamo 1.2 肉夹馍 MethodContext async Test 返回值 OnExit

肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应用启动的初始化时间让服务更快可用,同时还能对静态方法进行AOP。

1.0.0 版本中,肉夹馍提供了最基础的AOP功能,可以进行日志记录和APM埋点。在 1.1.0 版本中新增了对更加实用的AOP操作的支持,可以进行异常处理和修改返回值。本次的 1.2.0 版本没有新增功能,主要是对 1.1.0 版本的增强,新增了一个ExMoAttribute,这个Attribute可能会替代MoAttribute成为大家更常用的Attribute.

前言

这次无法直接从快速开始入手了,了解一下前因后果会让你对肉夹馍最初的设定和ExMoAttribute的出现所解决的问题有更清晰的认识,这样也方便后续使用时能够明确的知道是应该使用MoAttribute还是ExMoAttribute.

在1.1.0版本发布后陆续收到两个issue,都是反馈在没有使用async语法的Task/ValueTask返回值的方法无法正确的在方法执行成功(OnSuccess)和方法退出前(OnExit)执行织入的代码,比如记录方法的执行耗时的示例:

static async Task Main(string[] args)
{
    await Test();
}

[Timeline]
static Task Test()
{
    Console.WriteLine($"{nameof(Test)} start");
    return Task.Run(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine($"{nameof(Test)} end");
    });
}

class TimelineAttribute : MoAttribute
{
    private Stopwatch _stopwatch;

    public override void OnEntry(MethodContext context)
    {
        _stopwatch = Stopwatch.StartNew();
        Console.WriteLine($"{context.Method.Name} {nameof(OnEntry)}");
    }

    public override void OnExit(MethodContext context)
    {
        _stopwatch.Stop();
        Console.WriteLine($"{context.Method.Name} {nameof(OnExit)} - {_stopwatch.ElapsedMilliseconds}ms");
    }
}

期望的输出可能是下面这样,等Test方法返回值Task执行完成之后再执行OnExit,统计耗时到返回的Task执行完毕之后:

Test OnEntry
Test start
Test end
Test OnExit - 1096ms

而实际的输出是下面这样的,方法的耗时仅为Task对象创建后返回的执行耗时,并没有等待Task执行:

Test OnEntry
Test start
Test OnExit - 96ms
Test end

这种表现其实是最开始设计时的设定。在我们刚接触async/await语法时,我们或许有听到这样的介绍“async/await让我们像写同步方法那样去写异步方法”。是的,有了async/await,我们就不用像以前EAP/APM那样去编写callback了,代码整体看起来和同步代码无异,同时我们也会注意到一点,异步方法的返回值类型是Task/ValueTask,但是我们实际return的对象类型却是其泛型参数类型(Task/ValueTask中的那个T),肉夹馍采用了这一设定。所以对于返回值时Task/ValueTask的方法,如果使用了async/await语法,那么你通过MethodContext.RealReturnType获取到的返回值类型就是其泛型参数类型(没有泛型参数时就是void),同时通过MethodContext.HandledExceptionMethodContext.ReplaceReturnValue设置/修改返回值时,返回值的类型也是Task/ValueTask的泛型参数类型。而对于没有使用async/await语法的方法,那么返回值类型就是Task/ValueTask本身。也可以简单的理解为MoAttribute里采用的方法返回值类型与你编写代码时return的对象类型相同。也因为这样的设定,在上面的示例中由于没有使用async/await语法,其实际的返回值类型就是Task,并不会去等待Task执行完毕,所以有了上面那段代码的执行效果。

如果希望上面那段代码达到预期的效果,有没有什么方案呢?答案是:有的

// 仅对TimelineAttribute的OnExit方法进行改造
class TimelineAttribute : MoAttribute
{
    // ...

    public override void OnExit(MethodContext context)
    {
        if (typeof(Task).IsAssignableFrom(context.RealReturnType))
        {
            ((Task)context.ReturnValue).ContinueWith(t => _OnExit());
        }
        else
        {
            _OnExit();
        }

        void _OnExit()
        {
            _stopwatch.Stop();
            Console.WriteLine($"{context.Method.Name} {nameof(OnExit)} - {_stopwatch.ElapsedMilliseconds}ms");
        }
    }
}

上面的方案通过自行判断返回值类型,对继承自Task的返回值通过显式转换后调用ContinueWith达到需要的效果。这个思路是通用的,但对有些需求实现起来就比较麻烦并且需要对肉夹馍的执行逻辑有一定的了解,比如异常处理,上面的例子中如果在Task.Run之前抛出异常,你需要在OnException中进行异常处理,而如果是在Task.Run里的Action中抛出异常,那就需要到OnSuccess中通过ContinueWith判断Task是否执行异常:

[Timeline]
static Task Test()
{
    Console.WriteLine($"{nameof(Test)} start");
    // throw new Exception(); // 这里抛出异常在OnException中处理
    return Task.Run(() =>
    {
        // throw new Exception(); // 这里抛出异常在OnSuccess中通过ContinueWith处理
        Thread.Sleep(1000);
        Console.WriteLine($"{nameof(Test)} end");
    });
}

在两个issue提出之后,细细想来这种需求或许才是大家最常用的,有时不使用async语法,可能仅仅是因为方法的重载只需对参数稍作处理然后直接调用重载方法即可,这种情况下不使用async语法也是很正常的。针对这类情况,就有了本次版本推出的ExMoAttribute了。

ExMoAttribute

ExMoAttribute的目标是解决前面提到的问题,对没有使用async语法的方法采用MoAttribute中使用了async语法相同的逻辑。

快速开始

# 添加NuGet引用
dotnet add package Rougamo.Fody
class TimelineAttribute : ExMoAttribute
{
    private Stopwatch _stopwatch;

    protected override void ExOnEntry(MethodContext context)
    {
        _stopwatch = Stopwatch.StartNew();
        Console.WriteLine($"{context.Method.Name} {nameof(OnEntry)}");
    }

    protected override void ExOnExit(MethodContext context)
    {
        _stopwatch.Stop();
        Console.WriteLine($"{context.Method.Name} {nameof(OnExit)} - {_stopwatch.ElapsedMilliseconds}ms");
    }
}

TimelineAttribute改成上面的代码即可完成最初统计耗时的需求了,应用了该Attribute的方法无论是否使用async语法都能达到同样的效果,再也不用去判断MethodContext.RealReturnType了。

ExMoAttribute的使用差异(重要)

ExMoAttributeMoAttribute除了类名和方法名有所区别之外,在使用时也有些许区别,下面是使用ExMoAttribute时与之前不同的地方:

  • 使用MethodContext.ExReturnValue获取方法返回值。如果是没有使用async语法的方法,使用之前的MethodContext.ReturnValue获取返回值,你获取到的会是Task/ValueTask类型的返回值,而不是其泛型参数类型;
  • 使用MethodContext.ExReturnType获取返回值类型。如果你要修改返回值或处理异常,你设置的返回值类型需要与MethodContext.ExReturnType相同;
  • 使用MethodContext.ExReturnValueReplaced获取返回值是否被修改。如果是没有使用async语法的方法,使用之前的MethodContext.ReturnValueReplaced获取到的一直都会是true,因为返回值被ContinueWith返回的Task替换了。

当前版本除了上面三个属性在使用时需要注意,其他的与MoAttribute基本无异,包括处理异常时依旧调用MethodContext.HandledException方法,修改/设置返回值时依旧调用MethodContext.ReplaceReturnValue方法,后续还会不会有其他差异就需要大家关注一下版本日志了。

标签:Task,Rougamo,1.2,肉夹馍,MethodContext,async,Test,返回值,OnExit
From: https://www.cnblogs.com/nigture/p/16705738.html

相关文章

  • 1.2w+字!Java IO 基础知识总结
    IO流简介IO即 Input/Output ,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称......
  • [uvm sequence专题] objection in sequence (sequence中objection的用法以及UVM1.1d 1
    objectioninsequence(sequence中objection的用法以及UVM1.1d1.2的区别)1前言在UVM中,除了在各个taskphase中会出现控制objection的情况,在defaultsequence的执行中......
  • 11.2单继承
    classAnimal:def__init__(self,name,hp,ad):self.name=nameself.hp=hpself.ad=addefeat(self):'吃药方法's......
  • 二进制部署k8s集群v1.23.9版本-17-安装minio
    17.1、准备minio镜像192.168.1.200操作dockerpullminio/minio:latestdockerimages|grepminiodockertage31e0721a96bharbor.qgutech.com/middleware/minio:lates......
  • 2061:【例1.2】梯形面积
    时间限制:1000ms内存限制:65536KB提交数:73419通过数:28762【题目描述】在梯形中阴影部分面积是150平方厘米,求梯形面积。 【输入】(无)【输出】输出梯形面积(......
  • ERROR server: 192.168.1.22:22122, response status 28 != 0以及2 != 0
    背景介绍:fdstdfs部署好以后进行测试,然后突然报错,记录一下本次的问题报错信息如下:   原因:经查找28!=0的问题应该是物理内存不够的原因,如下图,我的系统部......
  • 《GB13511.2-2011》PDF下载
    《GB13511.2-2011配装眼镜第2部分:渐变焦》PDF下载《GB13511.2-2011》简介GB13511的本部分规定了渐变焦配装眼镜的术语和定义、要求、试验方法、渐变焦镜片标记、标识......
  • Artlantis studio 2021.2 for Mac(3D渲染工具)
    Artlantisstudio2021forMac是Mac平台上一款专业的3D高级渲染器,artlantismac版专为设计师和建筑师所设计,能够为您快速创建出VR全景,动画和3D透视图等效果。artl......
  • kubernets之新版本v1.24.1
    原本计划于4月19日发布的Kubernetes1.24,一直延迟到5月3日才发布。此版本带来了46项增强,其中13项升级到稳定版,14项是不断改进的现有功能,13项是全新的,6项已弃用的功能。主......
  • Debian 11 安装 Nginx 1.22.0
    阿里云轻量级服务器Debian11安装Nginx1.22.0一、从官网下载nginx压缩包sudowgethttps://nginx.org/download/nginx-1.22.0.tar.gz-O/opt/nginx-1.22.0.tar.gz......