丰台石家庄网站建设,logo注册流程及费用,wordpress 图片加链接地址,创办网站需要什么肉夹馍是什么肉夹馍通过静态代码织入方式实现AOP的组件。.NET常用的AOP有Castle DynamicProxy、AspectCore等#xff0c;以上两种AOP组件都是通过运行时生成一个代理类执行AOP代码的#xff0c;肉夹馍则是在代码编译时直接修改原始方法IL代码#xff0c;在原始方法内织入AOP… 肉夹馍是什么肉夹馍通过静态代码织入方式实现AOP的组件。.NET常用的AOP有Castle DynamicProxy、AspectCore等以上两种AOP组件都是通过运行时生成一个代理类执行AOP代码的肉夹馍则是在代码编译时直接修改原始方法IL代码在原始方法内织入AOP代码的。.NET静态AOP的组件或许有人使用过PostSharp这是一个功能完善且强大的静态代码织入组件Postsharp有社区版但可惜的是社区版不支持异步方法肉夹馍的实现方式与Postsharp类似同时也支持了异步方法如果你仅仅使用了Postsharp方法层级的AOP代码织入功能可以尝试使用肉夹馍来替代Postsharp。快速开始# 添加NuGet引用
dotnet add package Rougamo.Fody// 1.定义类继承MoAttribute在该类中定义你在方法执行各阶段需要织入的代码
public class LoggingAttribute : MoAttribute
{public override void OnEntry(MethodContext context){// 从context对象中能取到包括入参、类实例、方法描述等信息Log.Info(方法执行前);}public override void OnException(MethodContext context){Log.Error(方法执行异常, context.Exception);}public override void OnExit(MethodContext context){Log.Info(方法退出时不论方法执行成功还是异常都会执行);}public override void OnSuccess(MethodContext context){Log.Info(方法执行成功后);}
}// 2.在需要织入代码的方法上应用LoggingAttribute
public class Service
{[Logging]public static int Sync(Model model){// ...}[Logging]private async TaskData Async(int id){// ...}
}通过实现空接口的方式进行代码织入在上面的示例中我们通过在方法上应用Attribute进行AOP这种方式目标明确但有些AOP代码我们可能希望应用于某一场景或某一层级每个方法都去应用Attribute很繁琐而且代码侵入严重。此时就可以考虑使用实现空接口IRougamo的方式进行批量Attribute应用public interface IService : IRougamoLoggingAttribute { }public interface IMyService : IService { }public class MyService : IMyService
{
}上面的示例中MyService所有的public实例方法都将应用LoggingAttribute你可能注意到我标红的部分了为什么是public实例方法呢这是默认值你可以在继承MoAttribute时通过重写Flags属性来修改这一默认值比如下面的示例中FullLoggingAttribute将会应用于所有方法。另外需要注意的是Flags属性在Attribute直接应用到方法上时是无效的比如LoggingAttribute默认仅应用public实例方法但像快速开始里的代码那样Async方法虽然是private的但还是会应用LoggingAttributepublic class FullLoggingAttribute : LoggingAttribute
{public override AccessFlags Flags AccessFlags.All;
}实例-Rougamo.OpenTelemetry在快速开始里介绍了肉夹馍两种常用的使用方式更多的使用方式可以到github查看readme在本篇文章中就不再做更多介绍了接下来我将介绍使用肉夹馍的一个项目Rougamo.OpenTelemetry如果你准备使用肉夹馍但你还是不太清楚具体应该怎么使用可以参考这个项目的代码实现。关于OpenTelemetry在了解OpenTelemetry前你需要先了解APM(Application Performance Management/Monitor)在这个微服务的时代APM已经成为了必不可少的一部分没有它整个系统对我们而言就是一个黑盒你无法得知一个请求在微服务之间是如何调用如何完成难以排查一个用户超时是哪个服务超时或出错。现在市面上有很多开源的APM比如Pinpoint, Zipkin, SkyWalking, CAT, jaeger等虽说大家基本都是参考google的dapper论文设计出来的但实现和功能侧重却大相径庭为了对此形成一个规范先后出现了OpenTracing和OpenCensus并在此后合并为现在的OpenTelemetry。OpenTelemetry的出现为APM的接入提供了一种可能“应用不需要在意具体的APM服务端使用的是Zipkin还是jaeger或是其他的情况下应用只需要使用OpenTelemetry的SDK进行埋点APM通过实现OTLP(OpenTelemetry Protocol)来支持OpenTelemetry数据格式即可”当前已经有些APM完全采用OpenTelemetry SDK作为默认的SDK比如jaeger也有部分支持的APM比如skywalking。关于Rougamo.OpenTelemetry现在大部分流行的APM都有对应语言的SDK并且还实现了常用的I/O组件埋点opentelemetry-dotnet也已经提供了包括HttpClient、SqlClient、AspNetCore等I/O埋点。虽说一般而言服务的耗时一般就在I/O部分但由于开发人员的代码习惯不同、代码水平不同以及业务复杂度等情况某些非I/O代码也会产生一定的耗时同时在一个接口中可能会执行多次I/O操作如果仅仅只有I/O埋点可能很难分辨层次关系此时可能需要一些本地辅助埋点Rougamo.OpenTelemetry便是用于添加本地埋点的组件。快速开始# 启动项目引用Rougamo.OpenTelemetry.Hosting
dotnet add package Rougamo.OpenTelemetry.Hosting
# 添加埋点的项目引用Rougamo.OpenTelemetry
dotnet add package Rougamo.OpenTelemetrypublic class Startup
{public void ConfigureServices(IServiceCollection services){// ...services.AddOpenTelemetryTracing(builder {builder.AddRougamoSource() // 初始化Rougamo.OpenTelemetry.AddAspNetCoreInstrumentation().AddJaegerExporter();});// 修改Rougamo.OpenTelemetry默认配置services.AddOpenTelemetryRougamo(options {options.ArgumentsStoreType ArgumentsStoreType.Tag;});}
}class Service
{[return: ApmIgnore] // 返回值不记录[Otel] // 默认记录参数和返回值需要通过ApmIgnoreAttribute来忽略不需要记录的参数或返回值public async Taskstring M1([ApmIgnore] string uid, // 该参数不记录DateTime time){// do somethingreturn string.Empty;}[PureOtel] // 默认不记录参数和返回值需要通过ApmRecordAttribute来记录指定的参数或返回值public void M2([ApmRecord] double d1, // 记录该参数double d2){// do something}
}// 通过实现空接口织入
public interface ITestService : IRougamoFullOtelAttribute
{// ...
}
public class TestService : ITestService
{// ...
}Rougamo.OpenTelemetry的埋点会对应生成一个名称为方法全名称(ClassFullName.MethodName)的LocalSpan根据你使用的是OtelAttribute还是PureOtelAttribute决定默认是否记录参数和返回值。Rougamo.OpenTelemetry是用来丰富APM埋点的但是切记不要过度添加埋点过多的埋点会让你的trace看起来很臃肿。关于Rougamo.OpenTelemetry更多的使用说明详见githubgithub上的代码中包含了一个jaeger的示例代码你可以从jaeger官网上下载一个all-in-one包快速运行一个jaeger服务端然后启动示例项目访问http://localhost:5000/test接口最后访问jaeger uihttp://localhost:16686查看刚刚访问的test接口的trace数据。更多关于关于肉夹馍的应用情况写肉夹馍的动机是公司在使用postsharp做AOP起初公司的代码是framework的并且基本使用同步方法所以postsharp的免费版本是足足够用的随着.NET的发展公司的代码也逐渐从同步发展到异步从framework发展到core然后我们通过购买付费版本的postsharp也能继续维持着不过由于个人对postsharp的实现产生了兴趣所以悄悄的建立了这个项目但是由于个人比较懒这个早在19年就建立了的项目直到21年才完成。在发布1.0.1之前项目一直处于闭源状态但在闭源状态下已经在公司内部发布了几个测试版本其中1.0.0版本已经在公司测试环境沉淀了一个季度有余现在已经将1.0.0版本发布到了线上使用中发布在nuget.org上的1.0.1版本相对于1.0.0版本在代码上没有任何修改。Rougamo项目的TargetFramework是netstandard2.0公司应用了Rougamo的项目都是.NET Core3.1的所以如果你的项目是.NET Core3.1的你可以相对放心的使用如果不着急应用也推荐测试环境沉淀一下如果你是其他版本那么推荐你在测试环境沉淀一段时间肉夹馍作为一个新项目可能还会存在一些未知BUG如果有任何BUG请反馈到github issue中。关于.NET的静态代码织入.NET的静态代码织入其实我了解的也不是特别多我知道鼻祖应该是Mono.Cecil百度也能搜到很多它的介绍然后就是很强大但大部分功能收费的Postsharp以及对Mono.Cecil进行封装使其更易用的Fody肉夹馍便是使用Fody实现AOP代码织入的。静态代码织入在我观察下来使用得并不是很普遍这或许是因为动态代理早已成熟的缘故吧。那么静态织入相对于动态代理有什么优势呢说实话开发肉夹馍很大一部分原因是个人兴趣但这并不代表它没有优势静态织入是在编译时进行的静态织入只会让编译时间稍长些许而动态代理的方式都是在应用启动时动态生成代理类来实现的这个过程必定会占用些许时间并且在这个初始化动作完成前服务是不会进入就绪状态的也就是这个服务暂时为不可用状态的服务初始化时间越短服务整体的可用性就会越好这就是静态织入带来的优势。当然有些朋友可能会认为这是在钻牛角尖确实很多时候我们可能认为这种耗时是微乎其微的事实也确实如此但做基础架构关注的就是这些微乎其微耗时我们经常能看到java的一些技术博文上会写到他们做了很多字节码层面的优化他们的这种优化很多时候只是优化了那么几个指令单拎出来看着似乎没有多大的性能提升然而在大流量高吞吐的服务中这样优化的效果将会显现出来静态织入也是如此性能就是这样一点一点扣出来的。关于Fody.NET的开发者应该或多或少都听说甚至使用过ABP它是.NET中非常流行的一套DDD框架了如果你还看过ABP的源码你或许见过Fody的影子是的ABP也有使用到Fody使用的是ConfigureAwait.Fody我们在编写异步方法的时候经常会增加一个.ConfigureAwait(false)ConfigureAwait.Fody的功能就是为异步调用默认加上这个方法调用。进入到Fody的github首页你将能看到很多借助于Fody开发的组件我们也可以直接在nuget.org上以Fody为关键字进行搜索你将能看到更多以Fody开发的组件同时你可能还会发现在下载量很高的NuGet包中有两个AOP相关实现MethodDecorator.Fody和MethodBoundaryAspect.Fody早在我建立肉夹馍这个项目前我就看到了这两个项目但当时的他们没有对异步方法的支持就在这篇文章写到这里的时候我再次去查看了这两个项目他们对异步的支持依旧不能满足我的需求他们的OnExit方法都是在状态机在第一次返回也就是在遇到第一个await的时候执行的这时候这个异步方法实际上可能并没有执行完毕下面我会给一个例子各位可以自己进行尝试。关于为什么我没有直接参与他们的项目而是自己新建了一个项目主要有两个原因一是我有一丢丢懒不确定这个项目我会投入多少精力并且什么时候去完成事实也正如我的预期两年过去了二是我的英语有一丢丢差IL方面我也不算老手我担心有些问题交流起来有困难所以最终也就独立建了肉夹馍这个项目了。dotnet add package Rougamo.Fody
dotnet add package MethodDecorator.Fody
dotnet add package MethodBoundaryAspect.Fody!--FodyWeavers.xml--
Weavers xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:noNamespaceSchemaLocationFodyWeavers.xsdRougamo /MethodDecorator /MethodBoundaryAspect /
/Weaversclass Program
{static async Task Main(string[] args){try{await Async();}catch{}}[RougamoLog]//[MethodDecoratorLog]//[MethodBoundaryAspectLog]static async Task Async(){Console.WriteLine(1);await Task.Delay(10);Console.WriteLine(2);throw new NotImplementedException(not implemented);}
}分别用三个Attribute运行上面的程序你会得到下面的输出肉夹馍的异常信息是在输出2之后输出exit信息在最后输出也就是异步方法执行完毕后MethodDecorator没有捕获到异步的异常并且exit信息在输出2之前就输出了MethodBoundaryAspect捕获到了异步的异常信息但是exit信息在输出2之前输出了也就是你无法在异步方法真正执行完毕后织入代码。[Rougamo] on entry
1
2
[Rougamo] on exception: not implemented
[Rougamo] on exit[MethodDecorator] on init
[MethodDecorator] on entry
1
[MethodDecorator] on exit
2[MethodBoundaryAspect] on entry
1
[MethodBoundaryAspect] on exit
2
[MethodBoundaryAspect] on exception: not implemented关于使用肉夹馍开发组件的注意事项最后如果你准备使用肉夹馍并且你准备使用肉夹馍开发一个供他人使用的NuGet组件那么你需要把项目文件(.csproj)中Rougamo.Fody的引用改成下面这样不然你发布的NuGet其他人引用后将需要额外引用Fody否则将无法进行代码织入具体可以参考Rougamo.OpenTelemetryPackageReference IncludeRougamo.Fody Version1.0.1 IncludeAssetsall PrivateAssetscontentfiles;analyzers /最后的最后即使你不准备使用肉夹馍也希望通过此文让你了解到静态代码织入了解到Mono.Cecil和Fody如果.NET能够发展壮大起来那么静态代码织入也终将得到更大的发展。这篇文章中不论是Rougamo还是Rougamo.OpenTelemetry都没有进行完整的介绍如果你准备使用它们请移步github了解更多。