原文:Richard Lander
翻译:Edi Wang
今天,我们宣布 .NET Core 3.0 Preview 6。它的更新包括编译程序集以改进启动、使用链接器和 EventPipe 改进优化应用程序的大小。我们还在 ARM64 上发布了针对 Alpine 的新 Docker 镜像。
立即在 Windows、macOS 和 Linux 上下载 .NET Core 3.0 Preview 6。
https://dotnet.microsoft.com/download/dotnet-core/3.0
发行说明已在 dotnet/core 发布。Preview 5 和 6 之间的 API 差异也在此处。
译者注:
发型说明:https://github.com/dotnet/core/blob/master/release-notes/3.0/preview/3.0.0-preview6.md
API 差异:https://github.com/dotnet/core/blob/master/release-notes/3.0/preview/api-diff/preview6/3.0-preview6.md
ASP.NET Core 以及 EF Core 今天也发布了更新。
https://devblogs.microsoft.com/aspnet/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6/
如果您错过了,请查看我们在 .NET Core 3.0 Preview 5 中发布的改进,这些改进是从上个月开始发布的。
WPF 及 Windows Forms 的改进
WPF 团队现已完成将大部分 WPF 代码库发布到 GitHub。事实上,他们刚刚发布了 15 个程序集的源代码。对于任何熟悉 WPF 的人,程序集名称应该非常熟悉。
在某些情况下,测试仍在在进行中,以便在 3.0 GA 之前发布。也就是说,所有这些代码的存在应使 WPF 社区能够充分参与跨 WPF 进行更改。从阅读一些 GitHub 问题可以明显看出,社区有自己的待办事项,它一直在等待实现。例如,黑色主题。
Alpine Docker 镜像
Docker 镜像现在可用于 ARM64 上的 .NET Core 和 ASP.NET Core。它们以前仅适用于 x64。
以下镜像可用于 Dockerfile 或 docker pull,如下:
docker pull mcr.microsoft.com/dotnet/core/runtime:3.0-alpine-arm64v8
docker pull mcr.microsoft.com/dotnet/core/aspnet:3.0-alpine-arm64v8
Event Pipe 改进
Event Pipe 现在支持多个会话。这意味着您可以使用事件侦听器在进程中使用事件,同时具有进程外事件管道客户端。
添加了新的 Perf 计数器:
- % GC 时间
- Gen 0 堆大小
- Gen 1 堆大小
- Gen 2 堆大小
- LOH 堆大小
- 分配速率
- 已加载的程序集数量
- 线程池线程数量
- 监视器锁争用率
- 线程池任务队列
- 线程池任务完成速率
探查器附加现在使用相同的 Event Pipe 基础结构实现。
请参阅 David Fowler 的《Playing with counters》,了解可以使用 Event Pipe 执行自己的性能调查或仅监视应用程序状态。
请参阅 dotnet-counters 以安装 dotnet-counters 工具。
译者注:
Playing with counters
https://twitter.com/davidfowl/status/1135355693634949121
dotnet-counters
https://github.com/dotnet/diagnostics/blob/master/documentation/dotnet-counters-instructions.md
使用ReadyToRun镜像优化 .NET Core 应用
通过将应用程序程序集编译为 ReadyToRun (R2R) 格式,可以缩短 .NET Core 应用程序的启动时间。R2R 是提前 (AOT) 编译的一种形式。
R2R 二进制文件通过减少 JIT 在应用程序加载时需要执行的工作量来提高启动性能。二进制文件包含与 JIT 生成的代码类似的本机代码,在性能最重要的时候(在启动时)给 JIT 一点假期。R2R 二进制文件较大,因为它们包含中间语言 (IL) 代码(某些方案仍然需要)和同一代码的本机版本,以改进启动。
.NET Core 3.0 支持 R2R。它不能与早期版本的 .NET Core 一起使用。
样例性能数据
以下是使用示例 WPF 应用程序收集的性能数字。该应用程序作为 self-contained 发布,并且不使用程序集链接器(稍后将介绍)。
译者注:
WPF应用程序 https://github.com/ridomin/msix-catalog
仅 IL 的应用程序
启动时间:1.9 秒
内存使用量: 69.1 MB
应用程序大小:150 MB
使用 ReadyToRun 镜像
启动时间:1.3 秒
内存使用量:55.7 MB
应用程序大小: 156 MB
ReadyToRun详解
R2R 可以同时编译库和应用程序二进制文件。目前,库只能作为应用程序的一部分编译 R2R,不能作为 NuGet 包进行分发。我们希望对该方案是否重要提供更多反馈。
AOT 编译程序集在很长一段时间内一直作为一个概念使用 .NET,可追溯到 .NET 框架和 NGEN。NGEN 有一个关键缺点,即必须在客户端计算机上使用 NGEN 工具进行编译。在应用程序构建中无法生成 NGEN 映像。
来到 .NET Core。它带有 crossgen,它生成名为 ReadyToRun 的较新格式的原生镜像。该名称描述了其主要价值主张,即这些本机映像可以作为构建的一部分生成,并且无需在客户端计算机上进行任何额外的工作即可"准备运行"。这是一个重大进步,也是风向变化的重要胜利。
在兼容性方面,ReadyToRun 镜像与 IL 程序集类似,存在一些关键差异。
IL 程序集仅包含 IL 代码。它们可以在支持该程序集的给定目标框架的任何运行时上运行。例如,netstandard2.0 程序集可以在任何受支持的操作系统(Windows、macOS、Linux)和体系结构(英特尔、ARM、32 位、64 位)上运行.NET Framework 4.6 和 .NET Core 2.0 。
R2R 程序集包含 IL 和本机代码。它们为特定的最小化 .NET Core 运行时版本和运行时环境 (RID) 编译。例如 .NET Standard 2.0 程序集可能是为 .NET Core 3.0 和 Linux x64 编译的 R2R。它仅在该配置或兼容配置(如 .NET Core 3.1 或 .NET Core 5.0,在 Linux x64 上)中可用,因为它包含仅在该运行时环境中可用的本机代码。
指示
ReadyToRun 编译是仅发布、选择加入的功能。我们和.NET Core 3.0 Preview 5一起发布了其预览版本。
要启用 ReadyToRun 编译,您必须:
- 将 PublishReadyToRun 属性设置为 true。
- 使用显式 RuntimeIdentifier 发布。
注意: 编译应用程序程序集时,生成的本机代码特定于平台和体系结构(这就是为什么在发布时必须指定有效的运行时标识符)。
下面是一个示例:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
</Project>
并使用以下命令发布:
dotnet publish -r win-x64 -c Release
注意:也可以在项目文件中设置RuntimeIdentifier。
注意: ReadyToRun 目前仅支持自包含(self-contained)应用。它将在以后的预览中为依赖于框架的应用启用。
通过将"PublishReadyToRunEmitSymbols"属性设置为 true,可以在项目中启用本机Symbol生成。不需要为调试目的生成本机Symbol。这些Symbol仅用于分析目的。
SDK 当前支持一种将某些程序集编译为 ReadyToRun 映像的方法。当某些程序集实际上不需要针对性能进行优化时,这可能很有用。这可以帮助减小应用程序的大小。对于 ReadyToRun 编译器无法编译特定程序集的情况,它也可以是一种有用的解决方法。使用"PublishReadyToRunExclude"项组完成排除。例子:
<ItemGroup>
<PublishReadyToRunExclude Include="FilenameOfAssemblyToExclude.dll" />
</ItemGroup>
跨平台/架构编译
ReadyToRun 编译器当前不支持交叉定位。您需要在给定目标上编译。例如,如果希望 Windows x64 的 R2R 镜像,则需要在该环境中运行发布命令。
例外情况:
Windows x64 可用于编译 Windows ARM32、ARM64 和 x86 镜像。
Windows x86 可用于编译 Windows ARM32 镜像。
Linux x64 可用于编译 Linux ARM32 和 ARM64 镜像。
程序集链接
.NET Core 3.0 SDK 附带了一个工具,可以通过分析 IL 和修剪未使用的程序集来减小应用的大小。
使用 .NET Core,始终可以发布包含运行代码所需的一切的自包含应用,而无需在部署目标上安装 .NET。在某些情况下,应用只需要框架的一小部分才能运行,并且可能通过仅包含已使用的库而缩小。
我们使用 IL 链接器扫描应用程序的 IL 以检测实际需要的代码,然后修剪未使用的框架库。这可以显著减小某些应用的大小。通常,类似工具的小型控制台应用受益最大,因为它们倾向于使用框架的较小子集,并且通常更便于修剪。
要使用此工具,请在项目中设置 PublishTrimmed=true,并在项目中发布自包含(self-contained)的应用:
dotnet publish -r <rid> -c Release
发布输出将包括框架库的子集,具体取决于应用程序代码调用的内容。对于 helloworld 应用,链接器将大小从 68MB 减少到 28MB。
使用反射或相关动态功能的应用程序或框架(包括 ASP.NET Core 和 WPF)在修剪时通常会中断,因为链接器不知道此动态行为,通常无法确定哪些框架类型在运行时进行反射所需的。要修剪此类应用,您需要告诉链接器代码中反射以及所依赖的任何包或框架中所需的任何类型。请务必在修剪后测试应用。
有关 IL 链接器的详细信息,请参阅文档,或访问单mono/linker仓库。
译者注:
文档:https://aka.ms/dotnet-illink
仓库:https://github.com/mono/linker
注意:在 .NET Core 的早期版本中,ILLink.Tasks 作为外部 NuGet 包提供,并提供了许多相同的功能。它不再受支持 – 请更新到最新的 3.0 SDK 并尝试新的体验!
将链接器和ReadToRun一起用
链接器和 ReadyToRun 编译器可用于同一应用程序。通常,链接器会使应用程序变小,然后随时运行的编译器会使其再次变大一点,但性能会显著提高。值得在各种配置中进行测试,以了解每个选项的影响。
注意: dotnet/sdk #3257阻止链接器和 ReadyToRun 一起使用到 WPF 和 Windows Forms 应用程序。我们正在努力修复它作为 .NET Core 3.0 版本的一部分。
原生托管示例
团队最近发布了原生托管示例。它演示了在原生应用程序中托管 .NET Core 的最佳做法方法。
作为 .NET Core 3.0 的一部分,我们现在向 .NET Core 原生托管公开常规功能,这些托管以前仅通过官方提供的 .NET Core 托管对 .NET Core 托管应用程序可用。该功能主要与程序集加载相关。此功能应使生成原生托管变得更容易,这些原生托管可以利用 .NET Core 的完整功能集。
译者注:
示例
https://github.com/dotnet/samples/tree/master/core/hosting/HostWithHostFxr
HttpClient的HTTP/2支持
HTTP/2 是 HTTP 协议的主要修订版。HTTP/2 的一些显著功能是支持标头压缩和通过同一连接完全多路复用流。虽然 HTTP/2 保留了 HTTP 的语义(HTTP 标头、方法等),但它在数据框架和通过无线方式发送方面与 HTTP/1.x 是一种变化。
HttpClient 现在添加用于发出 HTTP/2 请求的支持。当默认值仍为 HTTP/1.1 时,您可以通过在 HTTP 请求消息上设置版本来选择使用 HTTP/2。
var client = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };
// HTTP/1.1 request
using (var response = await client.GetAsync("/"))
{
Console.WriteLine(response.Content);
}
// HTTP/2 request
using (var request = new HttpRequestMessage(HttpMethod.Get, "/") { Version = new Version(2, 0) })
using (var response = await client.SendAsync(request))
{
Console.WriteLine(response.Content);
}
或者,您可以通过在 HttpClient 上设置默认请求版本属性来默认发送 HTTP/2 请求。
var client = new HttpClient()
{
BaseAddress = new Uri("https://localhost:5001"),
DefaultRequestVersion = new Version(2, 0)
};
// Defaults to HTTP/2
using (var response = await client.GetAsync("/"))
{
Console.WriteLine(response.Content);
}
由于框架的这种更改,服务器和客户端需要协商所使用的协议版本。应用程序层协议协商 (ALPN) 是一个 TLS 扩展,允许服务器和客户端协商作为 TLS 握手的一部分使用的协议版本。虽然服务器和客户端之间有可能在协议上事先知道,但大多数服务器仅支持 ALPN 作为建立 HTTP/2 连接的唯一方法。因此,HTTP/2 仅在 TLS 连接上由 HttpClient 协商。
在开发方案中,当服务器和客户端事先知道两者都讲 HTTP/2 未加密时,您可以通过设置 AppContext 开关或环境变量(DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2UNENCRYPTEDSUPPORT=1)在明文上建立 HTTP/2 连接。
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
尾声
请尝试新功能。请提交你发现的错误,问题或任何具有挑战性的经验。我们需要反馈!您也可以提交功能请求,但它们可能需要等待实现,直到此时的下一个版本。
现在,我们非常接近于完成 .NET Core 3.0 的功能,并且正在将团队的重点转移到发布的质量上。我们还有几个月的错误修复和性能工作。当我们完成这一过程时,我们也会感谢您的反馈。
请注意,我们将很快将 .NET Core 存储库的主分支切换到下一个主要版本,很可能在预览 7 发布(7 月)发布或之后。
感谢您尝试 .NET Core 3.0 预览版。我们感谢您的帮助。此时,我们专注于获得最终版本。