0 起因

这段日子看到dotnet7-rc1发布,我对NativeAot功能比较感兴趣,如果aot成功,这意味了我们的dotnet程序在防破解的上直接指数级提高。我随手使用asp.netcore-7.0模板创建了一个默认的web程序,发现aot发布出来,web服务完全使用,这是之前那些preview版本做不到的。想到fastgithub本质上也是基于asp.netcore-6.0框架的项目,于是走上fastgithub的aot改造之路。

1 改造步骤

1.1 升级框架

将所有项目的TargetFramework值改为7.0,fastgithub使用Directory.Build.props,所以我只需要在Directory.Build.props文件修改一个地方,所有项目生效了。

1.2 升级nuget包

所有项目的nuget包进行升级,像有些是6.0.x版本的,如果有7.0.x-rc.x.x的更新包,就升级到最新rc版本。

1.3 json序列化

如果您的使用JsonSerializer序列化了内部未公开的类型,则需要改为JsonSerializerContext(源代码生成)方式,比如我在想序列化下面的EndPointItem类型的实例,需要如下改进:

private record EndPointItem(string Host, int Port);

[JsonSerializable(typeof(EndPointItem[]))]
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
private partial class EndPointItemsContext : JsonSerializerContext
{
}
 var utf8Json = JsonSerializer.SerializeToUtf8Bytes(endPointItems, EndPointItemsContext.Default.EndPointItemArray);

2 aot发布

我发布在vs上进行发布时有问题,我们需要在使用cli来发布,cli发布还能为我们提供更多的编译信息输出。

2.1 单文件的发布命令

set output=./publish
if exist "%output%" rd /S /Q "%output%"
dotnet publish -c Release /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained -r win-x64 -o "%output%/fastgithub_win-x64" ./FastGithub/FastGithub.csproj

aot编译之后也是单个文件,所以如果您的程序使用PublishSingleFile模式发布不能正常运行的话,就不用试着aot发布了。

2.2 aot发布的命令

set output=./publish
if exist "%output%" rd /S /Q "%output%"
dotnet publish -c Release /p:PublishAot=true /p:PublishTrimmed=true --self-contained -r win-x64 -o "%output%/fastgithub_win-x64" ./FastGithub/FastGithub.csproj

我们只需要把之前的PublishSingleFile改为PublishAot,他们两个不能同时设置为true。经过几分钟的满屏黄色警告之后,我们终于得到aot版本的40MB左右的fastgtihub.exe,迫不及待地运行了fastgithub.exe,不幸的是程序运行异常:

Unhandled Exception: System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property.
---> System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property.
---> System.NotSupportedException: 'Org.BouncyCastle.Security.DigestUtilities+DigestAlgorithm[]' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility
at System.Reflection.Runtime.General.TypeUnifier.WithVerifiedTypeHandle(RuntimeArrayTypeInfo, RuntimeTypeInfo) + 0x5b
at System.Array.InternalCreate(RuntimeType, Int32, Int32*, Int32*) + 0x5c
at System.Array.CreateInstance(Type, Int32) + 0x46
at System.RuntimeType.GetEnumValues() + 0x86
at Org.BouncyCastle.Utilities.Enums.GetArbitraryValue(Type enumType) + 0xa
at Org.BouncyCastle.Security.DigestUtilities..cctor() + 0x86

2.3 尝试解决BouncyCastle

BouncyCastle是用于生成ca证书和服务器证书的第三方库,在dotnet6时或以前,我们没有其它库可以完成这个功能。以上的异常大概是提示了DigestUtilities这个类型的某个内部私有类型被裁剪了,所以无法创建这个已裁剪掉类型的数组类型。我想到可以给项目的ItemGroup加上<TrimmerRootAssembly Include="BouncyCastle.Crypto" />,让这个程序集不要裁剪,然后再进行新一轮aot编译,不幸的是这次是编译时异常:

CVTRES : fatal error CVT1103: 无法读取文件 [D:\github\FastGithub\FastGithub\FastGithub.csproj]
LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 [D:\github\FastGithub\FastGithub\FastGithub.csproj]
C:\Program Files\dotnet\sdk\7.0.100-rc.1.22431.12\Sdks\Microsoft.DotNet.ILCompiler\build\Microsoft.NETCore.Native.targe
ts(349,5): error MSB3073: 命令“"C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.34.31721\bin\Hostx
64\x64\link.exe" @"obj\Release\net7.0\win-x64\native\link.rsp"”已退出,代码为 1123。 [D:\github\FastGithub\FastGithub\FastGithu
b.csproj]

2.4 移除BouncyCastle

迫于无奈,我们必须移除对BouncyCastle的依赖,转为使用基础库来实现证书生成,这方面几乎没有任何可以查到有帮助的资料,我花了整整一天来改造,感兴趣证书生成的同学,可以参考CertGenerator.cs。去掉BouncyCastle之后再aot发布,程序可以运行起来了,没有任何异常,但是发现程序没有拦截任何流量。

2.5 查找程序不干活的原因

由于没有任何的异常输出,咱也不知道是啥情况,现在使用debug模式继续aot发布,然后运行fastgithub.exe,在vs附加到fastgithub进程,下断点分析。经过一路跟踪,我发现如下一个分支,总是进入return逻辑:

var domain = question.Name;
if (this.fastGithubConfig.IsMatch(question.Name.ToString()) == false)
{
return;
}

我想看看fastGithubConfig现在是什么值,为什么总是不匹配,但是经过aot之后,无法发现fastGithubConfig这个局部变量,而函数内的变量,也不再是crl类型,而是一种为调试而存在的代理类型一样,可看的信息也很少。

于是我加入大量的log,通过log看看fastGithubConfig是什么值,最后发现是配置绑定到Options的字典类型属性时,绑定不成功(但也没有任何异常或日志)。

2.6 解决配置绑定到字典的问题

这个问题咱实在不知道怎么解决,那就github上发起问题吧:services.Configure(configuration) failure at PublishAot,果然回复很积极,告诉咱们目前可以在任意调用的函数加上[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Dictionary<string, DomainConfig>))]。经过这么修改之后,配置绑定到Options生效了。

3 后续

经过这么一个实际项目aot之后,我对aot有了初步的了解,个人觉得aot基本可以用小型程序的发布,期待到dotnet8之后,NativeAot变成没有坑。

dotnet7 aot编译实战的更多相关文章

  1. .Net反编译实战

    原文:.Net反编译实战 当你面对一个已经部署好的网站,功能,性能都非常不给力的时候,你会怎么办? 当你尝试去了解这个网站业务逻辑,代码逻辑和数据库逻辑时却发现根本没有任何资料时你会怎么办? 当你准备 ...

  2. angular aot编译报错 ERROR in ./src/main.ts 解决方法

    昨天打包项目时遇到下图这样的错误: 开始以为了某些模块存在但未使用,折腾一番无果,后来升级angular-cli就搞定了,方法很简单: 1.删掉node_modules 2.更改package.jso ...

  3. JIT和AOT编译详解

    JIT和AOT编译介绍 JIT - Just-In-Time             实时编译,即时编译 通常所说的JIT的优势是Profile-Based Optimization,也就是边跑边优化 ...

  4. 知乎问题之:.NET AOT编译后能替代C++吗?

    标题上的Native库是指:Native分为静态库( 作者:nscript链接:https://www.zhihu.com/question/536903224/answer/2522626086 ( ...

  5. JIL 编译与 AOT 编译

    JIT:Just-in-time compilation,即时编译:AOT:Ahead-of-time compilation,事前编译. JVM即时编译(JIT) 1. 动态编译与静态编译 动态编译 ...

  6. 随笔:关于.net core单文件AOT编译

    .Net Core单文件发布已经很流畅了(vs已支持图形化操作发布). 但类似Go或者Graalvm JDK的完全事前编译为本地机器码的红能功能,还未发布于.net 6特性中,还处于实验室中. 另外, ...

  7. 编译实战 | 手摸手教你在Windows环境下运行Redis6.x

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是没事就愿意瞎捣鼓的Hydra. 不知道有没有小伙伴像我一样,平常开发中用的是windows操作系统,有时候想装点什么软件,一看 ...

  8. 设计模式课程 设计模式精讲 8-8 单例设计模式-Enum枚举单例、原理源码解析以及反编译实战

    1 课堂解析 2 代码演练 2.1 枚举类单例解决序列化破坏demo 2.2 枚举类单例解决序列化破坏原理 2.3 枚举类单例解决反射攻击demo 2.4 枚举类单例解决反射攻击原理 3 jad的使用 ...

  9. Xamarin.iOS项目编译提示Could not AOT the assembly

    Xamarin.iOS项目编译提示Could not AOT the assembly 错误信息:Could not AOT the assembly **************.dll 这个错误是 ...

随机推荐

  1. ReentrantLock 公平锁源码 第0篇

    ReentrantLock 0 关于ReentrantLock的文章其实写过的,但当时写的感觉不是太好,就给删了,那为啥又要再写一遍呢 最近闲着没事想自己写个锁,然后整了几天出来后不是跑丢线程就是和没 ...

  2. 2022 07 13 第一小组 田龙跃 Java再学习笔记

    1.类名命名规则: 只能由数字字母,下划线,美元符号组成(不能以数字开头,尽量不要用下划线开头) 2.注释(养成多写注释的好习惯) 单行注释 // ctrl+/ 多行注释 // ctrl+shirt+ ...

  3. 初学者对html的认知

    首先是关于本人对一个新事物的理解路线 对html的认知: what:首先我得知道它是什么?html是一门超文本标记语言,它不是一门编程语言 why:它为什么会存在?它存在的意义是什么?随着互联网的发展 ...

  4. 彻底理解DDS(信号发生器)的fpga实现(verilog设计代码)

    DDS(Direct Digital Synthesis)是一种把一系列数字信号通过D/A转换器转换成模拟信号的数字合成技术. 它有查表法和计算法两种基本合成方法.在这里主要记录DDS查表法的fpga ...

  5. Centos7较为彻底的删除mysql

    Centos7下较为彻底的删除mysql(root 身份操作) 删除mysql安装包 1. yum检查 yum list installed | grep mysql 安装则直接删除 示例:yum r ...

  6. VirtualBox 相关命令行

    简介 由于之前项目有一个需求,则是自动化创建虚拟机并加载相关配置,创建虚拟机无法避免对于虚拟机系统的设置,因此采用导入虚拟机的方式,并通过查阅virtualbox相关手册,知晓virtualbox可以 ...

  7. Jmix- 业务系统高效开发的新方式

    企业在数字化转型的过程中,都面临将现有的业务流程进行"软件化"的过程.然而,在我们的印象中,通常会觉得针对业务系统的软件开发不是特别高效.这背后有很多原因,从开发角度看,有一个主要 ...

  8. 开源图编辑库 NebulaGraph VEditor 的设计思路分享

    本文首发于 NebulaGraph 公众号 NebulaGraph VEditor 是一个拥有高性能.高可定制的所见即所得图可视化编辑器前端库. NebulaGraph VEditor 底层基于 SV ...

  9. 使用dotnet-monitor分析在Kubernetes的应用程序:Sidecar模式

    dotnet-monitor可以在Kubernetes中作为Sidecar运行,Sidecar是一个容器,它与应用程序在同一个Pod中运行,利用Sidecar模式使我们可以诊断及监控应用程序. 如下图 ...

  10. 年轻的樵夫哟,你掉的是这个免费 8 核 4G 公网Docker 服务器

    Play With Docker 直接打开 https://labs.ply-with-docker.com/ 即可访问 Play With Docker 平台. 注册一个 DockerHub 账号便 ...