【A】兼容Core3.0后 Natasha 的隔离域与热编译操作。

文章转载授权级别:A         预计阅读时间:15分钟

一、  2.0预览版本增加了哪些功能

大部分为底层的升级优化,例如:

  • 引擎兼容 Core3.0

  • 优化编译流程,增加编译前语法检测及日志,统一采用流加载方式

  • 在 Vito 的建议下改进了日志目录及命名

  • ALC 同类覆盖编译

  • 支持域的创建、卸载、锁操作

  • 支持共享域与独立域协作

  • 支持独立域的程序集创建、覆盖操作

  • 支持插件及依赖的加载

构建方面的强化,例如:

  • 支持枚举的构建和编译

  • 在 Vito 的建议下增加了多维数组反解器

  • 在 Vito 的建议下增加了锯齿数组反解器

  • 命名反解器支持锯齿和多维数组

二、我们经历了哪些实践

深度克隆:https://github.com/night-moon-studio/DeepClone

本项目由 Net_win、Vito、myFirstway、白开水组队开发,可在运行时动态生成克隆方法。深度克隆作为基础项目,锻炼了开源工作者的类型辨识技能,趟过了坑为以后的封装之路打下基础。

快速调用:https://github.com/night-moon-studio/NCaller

本项目由 AzulX 和 FUTURE* 开发,可以对运行时实体类、静态类的字段/属性进行动态调用和赋值,目前有两个主要分支,哈希二叉查找算法动态实现以及 FUTURE* 的指针二叉查找算法动态实现,在算法的动态实现上,Natasha 表现出了相当强大的优势。

三、谈一谈‘热更新’

'热更新'是 Core3.0 的亮点特性之一,不少小伙伴在看到译文的时候可能就已经想到了N多场景,历经两代 .NET 的洗礼,‘热更新’现在发展到什么样子了?下面简单谈一谈:

.NET Framework 开荒时期有 AppDomain 域之隔离术,包括有创建、加载程序集、卸载等方法,囊括百家程序集,一刀以斩之。对于前辈们来说谈到 AppDomain 可以口若悬河滔滔不绝,可惜我进入 C# 时间比较晚,对 AppDomain 的印象并不是很深,在应用上也没有什么造诣,仅此泛泛而言。

时间进入了 .NETCore 时代,AppDomain 在升级大潮中受到了致命打击, Create 方法和 Unload 方法经岁月升级后的源码中充斥着 throw 和 throw ,完全丧失了功能,取而代之的是 ALC(AssemblyLoadContext) ,Core3.0 的 ALC 是一个更为完善的操作类,官方为其定义了三大洪荒场景:

    1、插件编程

    2、动态编译,运行/刷新代码,网站/脚本引擎

    3、外部程序集的一次性内省(我个人理解就是类的信息,IsArray ,  IsClass 这种元数据只读属性)

据描述:Roslyn 之前一直用 AppDomain , 每个测试都腰酸背痛相当慢,自从换了 ALC( A blue Ca.) 一口气上5楼不费劲!官方画了大饼:未来 Roslyn 分析器执行编译时也都在ALC里进行,用完就卸载,卸磨就杀驴。

AppDomain 当初被定位在高性能、安全,历史证明这个定位跟 GPS 一样不准,ASP.NET 深受其害,历史车轮碾过了 ASP.NET 迎来了 ASP.NET Core ,在域功能被阉割的期间,ASP.NET Core 转向了相对静态的模型,增加了若干学习成本,详见 dotnet watch 命令。还有 Razor , 它从 .cshtml 编译到 .dll 的环境就是 ALC ,自建了一个名为 Razor-Server 的域环境。

另外还涉及到 LINQPad 和 Prism 框架, 精力有限,谁有兴趣就去研究研究吧。

ALC 的场景和案例可能激起了您的好奇心,下面讲一下 ALC 的应用:

我们可以在程序里创建多个 ALC 实例,但前提是你需要继承并实现它。每一个 ALC 的实例都是一个域(这里我就不叫它上下文了)。程序刚跑起来的时候是在 Defualt 域中的,这个域属于系统域卸不了,又称为共享域,不同域之间是无法访问和引用的不同域中信息的,却共用 Default 域中的信息,这个域至关重要,所以尽量避免向其中加载乱七八糟的程序集。

ALC 的使用需要注意以下几点:

    1、子类继承时需指定 ALC 的构造参数,base(isCollectible) , 这个参数可以赋予 ALC 卸载的能力。

    2、时刻注意反射信息的引用,只有清除引用,才能保证 ALC 实例被 GC 回收。

    3、在针对不同域的编程时可使用 EnterContextualReflection 方法锁住域内上下文,EnterContextualReflection 方法是放在 using 里的,这样你的花括号内就是一个域,并用 CurrentContextualReflectionContext 属性来获取当前操作域。

    4、注意 ALC 被线程占用的情况,被占用的对象是无法被回收的,如果你在测试中没有达到预期,除了排除代码问题之外你还需要注意函数是否被内联进入主线程或一个带有阻塞功能的线程,如果你不确定,可以在方法上使用 [MethodImpl(MethodImplOptions.NoInlining)] 阻止代码内联优化,正常情况下优化功能是开启的 。

    5、插件加载要注意与插件 dll 同目录的依赖文件,3.0 提供了 AssemblyDependencyResolver 操作类自动解析依赖,建议使用带有.deps.json文件的完整插件。

    6、当你的外部文件引用并使用了 Json.net/SqlConnection 等(测试日期9月3日),会造成不可回收的情况,不是你的代码出问题了,而是库本身的问题(待解决,3.1或者5.0)。

    

对 ALC 封装的一些建议:

    1、如果没有非托管代码,尽量不要在析构函数里折腾代码。

    2、如果你的域管理代码有些复杂,建议对外给个 IDispose 接口,以便清除对该域的程序集、元数据等信息的引用。

    3、肉眼观测内存时,测试代码中尽量不要在 Main 函数里做元数据的相关操作,主线程是 GC 的一个干扰点。

    4、若对内存的开销比较敏感,请尽可能分域,并结合弱引用实现创建与销毁。

    5、有时显式调用 Unload 方法会报异常,可以在 Dispose 里清除完引用之后再使用,实测你不用 Unload 方法也能回收。

Core3.0 中随 ALC 一起的还有反射的自省信息。

例如:MemberInfo.IsCollectible 、 Assembly.IsCollectible 等元数据,它将告诉你它是否能被回收,当然了这种自省的信息都是只读的。说到只读,.NET 中还存有一条进化路线即 :ReflectionOnlyLoad -> TypeLoader -> MetadataLoadContext (感谢WeiHanLi提供的信息), 只读元数据,相比 ALC 可执行,可调用,MLC ( MetadataLoadContext 在包 System.Reflection.MetadataLoadContext 中) 关注的是元数据只读操作,它并不能执行程序集的内容,仅仅反射出元数据,配套使用的是PathAssemblyResolver. 

对于无法卸载的情况,官方建议使用 windbg sos 组件进行调试,新版 sos 将独立出来,各位可以使用以下命令进行安装(建议开源工作者在封装此功能时添加UT测试检测卸载功能,尽可能保证在正常的情况下不需要用户自己去调试)。

$ dotnet tool install -g dotnet-sos --version 3.0.0-preview8.19412.1
$ dotnet-sos install

更多的实践还需要大家去探索。

四、Natasha是如何实现‘热更新’的

据以上信息,Natasha2.0 中动态构建遵循以下结构。

640?wx_fmt=png

640?wx_fmt=png

这两幅图说展示了 Natasha 中自定义编译域的结构,如果在创建程序集时不指定名字,程序集名将以 GUID 形式创建,故名随机程序集。在编译时未被移除的引用都将参与编译,该引用的来源:1、共享域;2、当前域;

  • 关于域的操作您可以

//创建一个域	
DomainManagment.Create("MyDomain");	
//移除一个域,移除将无法进行DomainManagment的其他任何操作	
DomainManagment.Remove("MyDomain");	
//判断域是否被卸载(被GC回收)	
DomainManagment.IsDeleted("MyDomain");	
//获取一个ALC上下文	
DomainManagment.Get("MyDomain");	//锁住已存在的域上下文	
using(DomainManagment.Lock("MyDomain"))	
{	var domain = DomainManagment.CurrentDomain;	//code in 'MyDomain' domain 	
}	
//创建并锁定一个域上下文	
using(DomainManagment.CreateAndLock("MyDomain"))	
{	var domain = DomainManagment.CurrentDomain;	//code in 'MyDomain' domain 	
}

  • 关于程序域的插件操作

//向域中注入插件 	
string dllPath = @"1/2/3.dll";	
var domain = DomainManagment.Get/Create("MyDomain");	
var assembly = domain.LoadFile(dllPath);	//锁域与插件解构操作	
string dllPath = @"1/2/3.dll";	
using(DomainManagment.CreateAndLock("MyDomain"))	
{	var (Assembly,TypeCache) = dllPath;	//Assembly: Assembly	//TypeCache: ConcurrentDictionary<string,Type> 	
}	//将引用从当前域内移除,下次编译将不会带着该程序集的信息	
//下面方法三选一均可实现引用移除操作	
domain.RemoveDll(dllPath);	
domain.RemoveAssembly(assembly);	
domain.RemoveType(type);

  • 关于程序集的操作:

//从指定域创建一个程序集操作实例	
var asm = domain.CreateAssembly("MyAssembly");	//向程序集中添加一段已经写好的类/结构体/接口/枚举	
asm.AddScript(@"using xxx; namespace xxx{xxxx}");	
asm.AddFile(@"Class1.cs");	//使用Natasha内置的操作类	
asm.CreateEnum(name=null);	
asm.CreateClass(name=null);	
asm.CreateStruct(name=null);	
asm.CreateInterface(name=null);	//使用Natasha内置的方法操作类	
//并不是很推荐使用这两个方法	
//建议在一个单独的程序集内编译方法 	
asm.CreateFastMethod(name=null);	
asm.CreateFakeMethod(name=null);	//使用程序集进行编译并获得程序集	
var assembly = asm.Complier();	
asm.GetType(name);

  • 结合域和程序集动态编译,实例

using(DomainManagment.CreateAndLock("MyDomain"))	
{	var domain = DomainManagment.CurrentDomain;	var assembly = domain.CreateAssembly("MyAssembly");	//创建一个接口	assembly	.CreateInterface("InterfaceTest")	.Using("System")	.OopAccess(AccessTypes.Public)	.OopBody("string ShowMethod(string str);");	//创建一个类并实现接口	assembly	.CreateClass("TestClass")	.Using("System")	.OopAccess(AccessTypes.Public)        	.Inheritance("InterfaceTest")	.Method(method => method	.MemberAccess(AccessTypes.Public)	.Name("ShowMethod")	.Param<string>("str")	.Body("return str+\" World!\";")	.Return<string>());	//编译并获取类型	var result = assembly.Complier();	var type = assembly.GetType("TestClass");	//Operator默认单独创建一个程序集	var @delegate = FastMethodOperator.New	.Using(type)	.MethodBody(@"	TestClass obj = new TestClass();	return obj.ShowMethod(arg);")	.Complie<Func<string, string>>();	@delegate("Hello");  //result = "Hello World!";	domain.Dispose();    //卸磨杀驴	
}

不要从公众号里复制代码到VS,会有意外字符。

五、Bug有缘人

访问以下链接:https://github.com/dotnet/corefx/blob/1b3a095c277476b746e6fb9884662cf12334c6a1/src/System.Reflection.MetadataLoadContext/src/System/Reflection/MetadataLoadContext.Loading.cs

640?wx_fmt=png

自右向左选中 MetadataLoadContext , 如果页面崩溃了, 老铁握爪。

https://github.com/dotnetcore640?wx_fmt=gif

打赏一杯酒,削减三分愁。跟着我们走,脱发包你有。

640?wx_fmt=png

640?wx_fmt=png

组织打赏账户为柠檬的账户,请标注「NCC」,并留下您的名字,以下地址可查看收支明细:https://github.com/dotnetcore/Home/blob/master/Statement-of-Income-and-Expense.md640?wx_fmt=png640?wx_fmt=png

OpenNCC,专注.NET技术的公众号

https://www.dotnetcore.xyz

640?wx_fmt=png

640?wx_fmt=png微信ID:OpenNCC

640?wx_fmt=png长按左侧二维码关注

欢迎打赏组织

给予我们更多的支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313977.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2019-03-22-算法-进化(回文链表)

题目描述 请判断一个链表是否为回文链表。 示例 1: 输入: 1->2 输出: false示例 2: 输入: 1->2->2->1 输出: true进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? 解题 思路1:直接利用List的顺序存储性&#x…

多项式牛顿迭代(应用:求逆,开根,对数exp)

多项式牛顿迭代 给定多项式g(x)g(x)g(x),求f(x)f(x)f(x),满足g(f(x))≡0(modxn)g(f(x)) \equiv 0 \pmod {x ^ n}g(f(x))≡0(modxn)。 泰勒展开 对于现有得f(x)f(x)f(x),构造一个多项式g(x)g(x)g(x),使得f(n)(x)g(n)(x)f^{(n)}(…

.NET Core 使用 K8S ConfigMap的正确姿势

背景ASP.NET Core默认的配置文件定义在 appsetings.json和 appsettings.{Environment}.json文件中。这里面有一个问题就是,在使用容器部署时,每次修改配置文件都需要重新构建镜像。当然你也可能会说,我的配置文件很稳定不需要修改&#xff0c…

2019-03-22-算法-进化(环形链表)

题目描述 给定一个链表,判断链表中是否有环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 示例 1: 输入&#xf…

ASP.NET Core on K8S深入学习(9)Secret Configmap

本篇已加入《.NET Core on K8S学习实践系列文章索引》,可以点击查看更多容器化技术相关系列文章。01—Secret关于Secret在应用启动过程中需要一些敏感信息,比如数据库用户名、密码,如果直接明文存储在容器镜像中是不安全的,K8S提供…

生成函数简单入门

生成函数 可表示为F(x)∑nankn(x)F(x) \sum\limits_{n} a_n k_n(x)F(x)n∑​an​kn​(x),对于不同类型的生成函数,有不同的核函数kn(x)k_n(x)kn​(x)。 普通生成函数:kn(x)xnk_n(x) x ^ nkn​(x)xn。 指数生成函数:kn(x)xnn!…

.NET Core 学习资料精选:进阶

2019.09月就要正式发布.NET 3.0了,对于前一篇博文《.NET Core 学习资料精选:入门》大家学的可还开心?这是本系列的第二篇文章:进阶篇,喜欢的园友速度学起来啊。对于还在使用传统.NET Framework 框架的园友,…

P4389 付公主的背包(生成函数,多项式exp)

P4389 付公主的背包 考虑生成函数有: ∏i1n11−xvi对其取对数得,∑i1nln⁡11−xviF(x)11−xv,G(x)ln⁡F(x)G(x)∫F′(x)F(x)dxG(x)∫vxv−11−xvdxG(x)∫∑n≥0vxvnv−1dxG(x)∑n≥0vxvnvvnvG(x)∑n≥0xv(n1)n1G(x)∑n≥1xvnn对于原式:∑i1n∑j1∞xvijj…

VS Code 1.38 发布!

今天(北京时间 2019 年 9 月 5 日),微软发布了 Visual Studio Code 1.38 版本。此版本主要更新的内容包括:Preserve case for global search and replace - 进行全局替换字符串时保留大小写。Settings editor string array valida…

HDU 5730 Shell Necklace(生成函数 多项式求逆)

Shell Necklace 由题意可得f[n]∑i1na[i]f[n−i]f[n] \sum\limits_{i 1} ^{n} a[i] f[n - i]f[n]i1∑n​a[i]f[n−i],设f[n]f[n]f[n]的生成函数为F(x)F(x)F(x),a[n]a[n]a[n]的生成函数为A(n)A(n)A(n) F(x)A(x)∑n≥0xn∑ijnaifn−i由于a00&#xff0c…

.NET Core 收徒,有缘者,可破瓶颈

最近感悟天命,偶有所得,故而打算收徒若干,以继吾之传承。有缘者,可破瓶颈,职场巅峰指日可待。入门基本要求:1、工作经验:1年或以上。2、拜师费用:3999元(RMB)…

【全】Docker(二)-在Docker中部署Nginx实现负载均衡视频教程

一、前言在前面的文章中我们已经介绍了如何在Centos7系统中安装Docker以及利用Docker进行Asp.Net Core应用的部署。在本文中,我们将继续介绍利用Docker部署Nginx服务实现负载均衡。文章最后附有Nginx部署的视频全过程。注:查看公众号历史文章&#xff0c…

P2012 拯救世界2(指数型生成函数)

P2012 拯救世界2 三种基因,我们分别列出其生成函数: F(x)∑n≥0xnn!exG(x)∑n≥0x2n1(2n1)!12(∑n≥0xnn!−∑n≥0(−1)nxnn!)12(ex−e−x)H(x)∑n≥0x2n(2n)!12(∑n≥0xnn!∑n≥0(−1)nxnn!)12(exe−x)F(x) \sum_{n \geq 0} \frac{x ^ n}{n!} e ^ x\…

开源导入导出通用库Magicodes.ExporterAndImporter发布

导入导出通用库 Magicodes.ExporterAndImporter为心莱团队封装的导入导出通用库,并且仍在跟随项目不断地打磨。GitHub地址:https://github.com/xin-lai/Magicodes.ExporterAndImporter目录特点相关官方Nuget包导出 Demo普通导出特性导出列头处理或者多语…

第一个错误的版本

题目描述 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。 假设你有 n 个版本 [1, 2, …, n],你…

P4705 玩游戏(生成函数,多项式ln)

P4705 玩游戏 有ansk∑i1n∑j1m(aibj)knm先舍弃nm不管ansk∑r0k∑i1n∑j1mCkrairbjk−r∑r0k∑i1n∑j1mk!r!(k−r)!airbjk−rk!∑r0k(1r!∑i1nair)(1(k−r)!∑j1mbjk−r)不难发现这就是一个卷积的形式了,但是我们现在还不知道∑i1nair,∑i1mbir,(r∈[0,k])设A(x)为∑…

爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数。 示例 1: 输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶…

netcore 中的动态代理与RPC实现(微服务专题)

一、关于RPC的调用1. 调用者(客户端Client)以本地调用的方式发起调用;  2. Client stub(客户端存根)收到调用后,负责将被调用的方法名、参数等打包编码成特定格式的能进行网络传输的消息体; …

多项式除法,多项式取模

多项式除法 给定一个nnn次多项式F(x)F(x)F(x)和mmm次多项式G(x)G(x)G(x),要求R(x),Q(x)R(x), Q(x)R(x),Q(x),满足F(x)R(x)G(x)Q(x)F(x) R(x)G(x) Q(x)F(x)R(x)G(x)Q(x)。 R(x)R(x)R(x)是一个n−mn - mn−m阶多项式,Q(x)Q(x)Q(x)是一个小于…

[翻译] .NET Core 3.0 Preview 9 发布

原文: Announcing .NET Core 3.0 Preview 9今天,我们宣布推出 .NET Core 3.0 Preview 9。就像 Preview 8 一样,我们专注于打磨 .NET Core 3.0 的最终版本,而不是添加新功能。如果这些最终版本看起来不像早期预览版那么令人兴奋,我…