日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

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

發(fā)布時(shí)間:2023/12/4 asp.net 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET静态代码织入——肉夹馍(Rougamo) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

肉夾饃是什么

肉夾饃通過(guò)靜態(tài)代碼織入方式實(shí)現(xiàn)AOP的組件。.NET常用的AOP有Castle DynamicProxy、AspectCore等,以上兩種AOP組件都是通過(guò)運(yùn)行時(shí)生成一個(gè)代理類執(zhí)行AOP代碼的,肉夾饃則是在代碼編譯時(shí)直接修改原始方法IL代碼,在原始方法內(nèi)織入AOP代碼的。.NET靜態(tài)AOP的組件或許有人使用過(guò)PostSharp,這是一個(gè)功能完善且強(qiáng)大的靜態(tài)代碼織入組件,Postsharp有社區(qū)版,但可惜的是社區(qū)版不支持異步方法,肉夾饃的實(shí)現(xiàn)方式與Postsharp類似,同時(shí)也支持了異步方法,如果你僅僅使用了Postsharp方法層級(jí)的AOP代碼織入功能,可以嘗試使用肉夾饃來(lái)替代Postsharp。

快速開(kāi)始

# 添加NuGet引用 dotnet add package Rougamo.Fody// 1.定義類繼承MoAttribute,在該類中定義你在方法執(zhí)行各階段需要織入的代碼 public class LoggingAttribute : MoAttribute {public override void OnEntry(MethodContext context){// 從context對(duì)象中能取到包括入?yún)ⅰ㈩悓?shí)例、方法描述等信息Log.Info("方法執(zhí)行前");}public override void OnException(MethodContext context){Log.Error("方法執(zhí)行異常", context.Exception);}public override void OnExit(MethodContext context){Log.Info("方法退出時(shí),不論方法執(zhí)行成功還是異常,都會(huì)執(zhí)行");}public override void OnSuccess(MethodContext context){Log.Info("方法執(zhí)行成功后");} }// 2.在需要織入代碼的方法上應(yīng)用LoggingAttribute public class Service {[Logging]public static int Sync(Model model){// ...}[Logging]private async Task<Data> Async(int id){// ...} }

通過(guò)實(shí)現(xiàn)空接口的方式進(jìn)行代碼織入

在上面的示例中,我們通過(guò)在方法上應(yīng)用Attribute進(jìn)行AOP,這種方式目標(biāo)明確但有些AOP代碼我們可能希望應(yīng)用于某一場(chǎng)景或某一層級(jí),每個(gè)方法都去應(yīng)用Attribute很繁瑣,而且代碼侵入嚴(yán)重。此時(shí)就可以考慮使用實(shí)現(xiàn)空接口(IRougamo<>)的方式進(jìn)行批量Attribute應(yīng)用

public interface IService : IRougamo<LoggingAttribute> { }public interface IMyService : IService { }public class MyService : IMyService { }

上面的示例中,MyService所有的public實(shí)例方法都將應(yīng)用LoggingAttribute,你可能注意到我標(biāo)紅的部分了,為什么是public實(shí)例方法呢?這是默認(rèn)值,你可以在繼承MoAttribute時(shí)通過(guò)重寫Flags屬性來(lái)修改這一默認(rèn)值,比如下面的示例中FullLoggingAttribute將會(huì)應(yīng)用于所有方法。另外需要注意的是Flags屬性在Attribute直接應(yīng)用到方法上時(shí)是無(wú)效的,比如LoggingAttribute默認(rèn)僅應(yīng)用public實(shí)例方法,但像快速開(kāi)始里的代碼那樣Async方法雖然是private的但還是會(huì)應(yīng)用LoggingAttribute

public class FullLoggingAttribute : LoggingAttribute {public override AccessFlags Flags => AccessFlags.All; }

實(shí)例-Rougamo.OpenTelemetry

在快速開(kāi)始里介紹了肉夾饃兩種常用的使用方式,更多的使用方式可以到github查看readme,在本篇文章中就不再做更多介紹了,接下來(lái)我將介紹使用肉夾饃的一個(gè)項(xiàng)目Rougamo.OpenTelemetry,如果你準(zhǔn)備使用肉夾饃,但你還是不太清楚具體應(yīng)該怎么使用,可以參考這個(gè)項(xiàng)目的代碼實(shí)現(xiàn)。

關(guān)于OpenTelemetry

在了解OpenTelemetry前,你需要先了解APM(Application Performance Management/Monitor),在這個(gè)微服務(wù)的時(shí)代,APM已經(jīng)成為了必不可少的一部分,沒(méi)有它整個(gè)系統(tǒng)對(duì)我們而言就是一個(gè)黑盒,你無(wú)法得知一個(gè)請(qǐng)求在微服務(wù)之間是如何調(diào)用如何完成,難以排查一個(gè)用戶超時(shí)是哪個(gè)服務(wù)超時(shí)或出錯(cuò)。現(xiàn)在市面上有很多開(kāi)源的APM比如Pinpoint, Zipkin, SkyWalking, CAT, jaeger等,雖說(shuō)大家基本都是參考google的dapper論文設(shè)計(jì)出來(lái)的,但實(shí)現(xiàn)和功能側(cè)重卻大相徑庭,為了對(duì)此形成一個(gè)規(guī)范,先后出現(xiàn)了OpenTracing和OpenCensus,并在此后合并為現(xiàn)在的OpenTelemetry。OpenTelemetry的出現(xiàn)為APM的接入提供了一種可能“應(yīng)用不需要在意具體的APM服務(wù)端使用的是Zipkin還是jaeger或是其他的情況下,應(yīng)用只需要使用OpenTelemetry的SDK進(jìn)行埋點(diǎn),APM通過(guò)實(shí)現(xiàn)OTLP(OpenTelemetry Protocol)來(lái)支持OpenTelemetry數(shù)據(jù)格式即可”,當(dāng)前已經(jīng)有些APM完全采用OpenTelemetry SDK作為默認(rèn)的SDK比如jaeger,也有部分支持的APM比如skywalking。

關(guān)于Rougamo.OpenTelemetry

現(xiàn)在大部分流行的APM都有對(duì)應(yīng)語(yǔ)言的SDK并且還實(shí)現(xiàn)了常用的I/O組件埋點(diǎn),opentelemetry-dotnet也已經(jīng)提供了包括HttpClient、SqlClient、AspNetCore等I/O埋點(diǎn)。雖說(shuō)一般而言服務(wù)的耗時(shí)一般就在I/O部分,但由于開(kāi)發(fā)人員的代碼習(xí)慣不同、代碼水平不同以及業(yè)務(wù)復(fù)雜度等情況,某些非I/O代碼也會(huì)產(chǎn)生一定的耗時(shí),同時(shí)在一個(gè)接口中可能會(huì)執(zhí)行多次I/O操作,如果僅僅只有I/O埋點(diǎn),可能很難分辨層次關(guān)系,此時(shí)可能需要一些本地輔助埋點(diǎn),Rougamo.OpenTelemetry便是用于添加本地埋點(diǎn)的組件。

快速開(kāi)始

# 啟動(dòng)項(xiàng)目引用Rougamo.OpenTelemetry.Hosting dotnet add package Rougamo.OpenTelemetry.Hosting # 添加埋點(diǎn)的項(xiàng)目引用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默認(rèn)配置services.AddOpenTelemetryRougamo(options =>{options.ArgumentsStoreType = ArgumentsStoreType.Tag;});} }class Service {[return: ApmIgnore] // 返回值不記錄[Otel] // 默認(rèn)記錄參數(shù)和返回值,需要通過(guò)ApmIgnoreAttribute來(lái)忽略不需要記錄的參數(shù)或返回值public async Task<string> M1([ApmIgnore] string uid, // 該參數(shù)不記錄DateTime time){// do somethingreturn string.Empty;}[PureOtel] // 默認(rèn)不記錄參數(shù)和返回值,需要通過(guò)ApmRecordAttribute來(lái)記錄指定的參數(shù)或返回值public void M2([ApmRecord] double d1, // 記錄該參數(shù)double d2){// do something} }// 通過(guò)實(shí)現(xiàn)空接口織入 public interface ITestService : IRougamo<FullOtelAttribute> {// ... } public class TestService : ITestService {// ... }

Rougamo.OpenTelemetry的埋點(diǎn)會(huì)對(duì)應(yīng)生成一個(gè)名稱為方法全名稱(ClassFullName.MethodName)的LocalSpan,根據(jù)你使用的是OtelAttribute還是PureOtelAttribute決定默認(rèn)是否記錄參數(shù)和返回值。Rougamo.OpenTelemetry是用來(lái)豐富APM埋點(diǎn)的,但是切記不要過(guò)度添加埋點(diǎn),過(guò)多的埋點(diǎn)會(huì)讓你的trace看起來(lái)很臃腫。
關(guān)于Rougamo.OpenTelemetry更多的使用說(shuō)明,詳見(jiàn)github,github上的代碼中包含了一個(gè)jaeger的示例代碼,你可以從jaeger官網(wǎng)上下載一個(gè)all-in-one包快速運(yùn)行一個(gè)jaeger服務(wù)端,然后啟動(dòng)示例項(xiàng)目,訪問(wèn)http://localhost:5000/test接口,最后訪問(wèn)jaeger uihttp://localhost:16686查看剛剛訪問(wèn)的test接口的trace數(shù)據(jù)。

更多關(guān)于

關(guān)于肉夾饃的應(yīng)用情況

寫肉夾饃的動(dòng)機(jī)是公司在使用postsharp做AOP,起初公司的代碼是framework的并且基本使用同步方法,所以postsharp的免費(fèi)版本是足足夠用的,隨著.NET的發(fā)展,公司的代碼也逐漸從同步發(fā)展到異步從framework發(fā)展到core,然后我們通過(guò)購(gòu)買付費(fèi)版本的postsharp也能繼續(xù)維持著,不過(guò)由于個(gè)人對(duì)postsharp的實(shí)現(xiàn)產(chǎn)生了興趣,所以悄悄的建立了這個(gè)項(xiàng)目,但是由于個(gè)人比較懶,這個(gè)早在19年就建立了的項(xiàng)目直到21年才完成。
在發(fā)布1.0.1之前,項(xiàng)目一直處于閉源狀態(tài),但在閉源狀態(tài)下已經(jīng)在公司內(nèi)部發(fā)布了幾個(gè)測(cè)試版本,其中1.0.0版本已經(jīng)在公司測(cè)試環(huán)境沉淀了一個(gè)季度有余,現(xiàn)在已經(jīng)將1.0.0版本發(fā)布到了線上使用中,發(fā)布在nuget.org上的1.0.1版本相對(duì)于1.0.0版本在代碼上沒(méi)有任何修改。Rougamo項(xiàng)目的TargetFramework是netstandard2.0,公司應(yīng)用了Rougamo的項(xiàng)目都是.NET Core3.1的,所以如果你的項(xiàng)目是.NET Core3.1的,你可以相對(duì)放心的使用(如果不著急應(yīng)用,也推薦測(cè)試環(huán)境沉淀一下),如果你是其他版本,那么推薦你在測(cè)試環(huán)境沉淀一段時(shí)間,肉夾饃作為一個(gè)新項(xiàng)目,可能還會(huì)存在一些未知BUG,如果有任何BUG請(qǐng)反饋到github issue中。

關(guān)于.NET的靜態(tài)代碼織入

.NET的靜態(tài)代碼織入其實(shí)我了解的也不是特別多,我知道鼻祖應(yīng)該是Mono.Cecil,百度也能搜到很多它的介紹,然后就是很強(qiáng)大(但大部分功能收費(fèi))的Postsharp,以及對(duì)Mono.Cecil進(jìn)行封裝,使其更易用的Fody,肉夾饃便是使用Fody實(shí)現(xiàn)AOP代碼織入的。
靜態(tài)代碼織入在我觀察下來(lái)使用得并不是很普遍,這或許是因?yàn)閯?dòng)態(tài)代理早已成熟的緣故吧。那么靜態(tài)織入相對(duì)于動(dòng)態(tài)代理有什么優(yōu)勢(shì)呢?說(shuō)實(shí)話,開(kāi)發(fā)肉夾饃很大一部分原因是個(gè)人興趣,但這并不代表它沒(méi)有優(yōu)勢(shì),靜態(tài)織入是在編譯時(shí)進(jìn)行的,靜態(tài)織入只會(huì)讓編譯時(shí)間稍長(zhǎng)些許,而動(dòng)態(tài)代理的方式都是在應(yīng)用啟動(dòng)時(shí)動(dòng)態(tài)生成代理類來(lái)實(shí)現(xiàn)的,這個(gè)過(guò)程必定會(huì)占用些許時(shí)間,并且在這個(gè)初始化動(dòng)作完成前,服務(wù)是不會(huì)進(jìn)入就緒狀態(tài)的,也就是這個(gè)服務(wù)暫時(shí)為不可用狀態(tài)的,服務(wù)初始化時(shí)間越短,服務(wù)整體的可用性就會(huì)越好,這就是靜態(tài)織入帶來(lái)的優(yōu)勢(shì)。當(dāng)然,有些朋友可能會(huì)認(rèn)為這是在鉆牛角尖,確實(shí),很多時(shí)候我們可能認(rèn)為這種耗時(shí)是微乎其微的,事實(shí)也確實(shí)如此,但做基礎(chǔ)架構(gòu)關(guān)注的就是這些微乎其微耗時(shí),我們經(jīng)常能看到j(luò)ava的一些技術(shù)博文上會(huì)寫到他們做了很多字節(jié)碼層面的優(yōu)化,他們的這種優(yōu)化很多時(shí)候只是優(yōu)化了那么幾個(gè)指令,單拎出來(lái)看著似乎沒(méi)有多大的性能提升,然而在大流量高吞吐的服務(wù)中,這樣優(yōu)化的效果將會(huì)顯現(xiàn)出來(lái),靜態(tài)織入也是如此,性能就是這樣一點(diǎn)一點(diǎn)扣出來(lái)的。

關(guān)于Fody

.NET的開(kāi)發(fā)者應(yīng)該或多或少都聽(tīng)說(shuō)甚至使用過(guò)ABP,它是.NET中非常流行的一套DDD框架了,如果你還看過(guò)ABP的源碼,你或許見(jiàn)過(guò)Fody的影子,是的ABP也有使用到Fody,使用的是ConfigureAwait.Fody,我們?cè)诰帉懏惒椒椒ǖ臅r(shí)候經(jīng)常會(huì)增加一個(gè).ConfigureAwait(false),ConfigureAwait.Fody的功能就是為異步調(diào)用默認(rèn)加上這個(gè)方法調(diào)用。
進(jìn)入到Fody的github首頁(yè)你將能看到很多借助于Fody開(kāi)發(fā)的組件,我們也可以直接在nuget.org上以Fody為關(guān)鍵字進(jìn)行搜索,你將能看到更多以Fody開(kāi)發(fā)的組件,同時(shí)你可能還會(huì)發(fā)現(xiàn),在下載量很高的NuGet包中有兩個(gè)AOP相關(guān)實(shí)現(xiàn)MethodDecorator.Fody和MethodBoundaryAspect.Fody,早在我建立肉夾饃這個(gè)項(xiàng)目前我就看到了這兩個(gè)項(xiàng)目,但當(dāng)時(shí)的他們沒(méi)有對(duì)異步方法的支持,就在這篇文章寫到這里的時(shí)候我再次去查看了這兩個(gè)項(xiàng)目,他們對(duì)異步的支持依舊不能滿足我的需求,他們的OnExit方法都是在狀態(tài)機(jī)在第一次返回也就是在遇到第一個(gè)await的時(shí)候執(zhí)行的,這時(shí)候這個(gè)異步方法實(shí)際上可能并沒(méi)有執(zhí)行完畢,下面我會(huì)給一個(gè)例子,各位可以自己進(jìn)行嘗試。關(guān)于為什么我沒(méi)有直接參與他們的項(xiàng)目,而是自己新建了一個(gè)項(xiàng)目,主要有兩個(gè)原因:一是我有一丟丟懶,不確定這個(gè)項(xiàng)目我會(huì)投入多少精力并且什么時(shí)候去完成,事實(shí)也正如我的預(yù)期,兩年過(guò)去了,二是我的英語(yǔ)有一丟丟差,IL方面我也不算老手,我擔(dān)心有些問(wèn)題交流起來(lái)有困難,所以最終也就獨(dú)立建了肉夾饃這個(gè)項(xiàng)目了。

dotnet add package Rougamo.Fody dotnet add package MethodDecorator.Fody dotnet add package MethodBoundaryAspect.Fody<!--FodyWeavers.xml--> <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"><Rougamo /><MethodDecorator /><MethodBoundaryAspect /> </Weavers>class 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");} }

分別用三個(gè)Attribute運(yùn)行上面的程序你會(huì)得到下面的輸出,肉夾饃的異常信息是在輸出2之后輸出,exit信息在最后輸出(也就是異步方法執(zhí)行完畢后);MethodDecorator沒(méi)有捕獲到異步的異常,并且exit信息在輸出2之前就輸出了;MethodBoundaryAspect捕獲到了異步的異常信息,但是exit信息在輸出2之前輸出了,也就是你無(wú)法在異步方法真正執(zhí)行完畢后織入代碼。

[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

關(guān)于使用肉夾饃開(kāi)發(fā)組件的注意事項(xiàng)

最后如果你準(zhǔn)備使用肉夾饃,并且你準(zhǔn)備使用肉夾饃開(kāi)發(fā)一個(gè)供他人使用的NuGet組件,那么你需要把項(xiàng)目文件(.csproj)中Rougamo.Fody的引用改成下面這樣,不然你發(fā)布的NuGet其他人引用后將需要額外引用Fody,否則將無(wú)法進(jìn)行代碼織入,具體可以參考Rougamo.OpenTelemetry

<PackageReference Include="Rougamo.Fody" Version="1.0.1" IncludeAssets="all" PrivateAssets="contentfiles;analyzers" />

最后的最后,即使你不準(zhǔn)備使用肉夾饃,也希望通過(guò)此文讓你了解到靜態(tài)代碼織入,了解到Mono.Cecil和Fody,如果.NET能夠發(fā)展壯大起來(lái),那么靜態(tài)代碼織入也終將得到更大的發(fā)展。這篇文章中不論是Rougamo還是Rougamo.OpenTelemetry都沒(méi)有進(jìn)行完整的介紹,如果你準(zhǔn)備使用它們,請(qǐng)移步github了解更多。

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的.NET静态代码织入——肉夹馍(Rougamo)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。