.NET Framework时代,.NET 应用程序大多直接部署运行在Windows服务器上,当然也可以通过Mono部署运行在Linux上。无论部署exe,还是IIS站点、或是Windows Service,编译后的程序直接copy、简单配置部署上即可。

有了.NET Core之后,.NET应用程序完美支持跨平台部署,.NET 5 Release后,沿袭了.NET Core应用程序发布模式。

支持跨平台部署运行,.NET 5/.NET Core的应用程序面临着多平台,多场景的部署需求。比如说:部署在Windows、Linux、MaxOS...,OS层面是否需要部署.NET Runtime运行时,.NET Runtime运行时的版本选择,等等。

因此,今天我们研究一下.NET 5/.NET Core应用程序的部署发布。

一、两种应用程序发布模式

  1. 以自包含的方式发布应用程序

这种模式包含.NET运行时和应用程序及其依赖项的应用程序。我们可以在未安装.NET运行时的操作系统上运行它。

总结一句话:把.NET Runtime运行时打包到程序运行目录中,应用程序运行的主机不需要安装.NET Runtime运行时。

  2. 以依赖于框架的方式发布应用程序

生成一个仅包含应用程序本身及其依赖项的应用程序。应用程序的运行环境必须单独安装.NET运行时。

总结一句话:不包含.NET Runtime运行时,只有应用程序本身和依赖的应用程序。应用程序运行的主机需要单独安装应用程序所需的.NET Runtime运行时。

二、NET 5/.NET Core的应用程序的发布指令:dotnet publish

dotnet publish -将应用程序及其依赖项发布到指定的文件夹中,以方便后续部署到目标托管系统。

关于dotnet publish的使用说明,可以参考以下链接:https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?WT.mc_id=DT-MVP-5003918

dotnet publish [<PROJECT>|<SOLUTION>] [-c|--configuration <CONFIGURATION>]
[-f|--framework <FRAMEWORK>] [--force] [--interactive]
[--manifest <PATH_TO_MANIFEST_FILE>] [--no-build] [--no-dependencies]
[--no-restore] [--nologo] [-o|--output <OUTPUT_DIRECTORY>]
[-p:PublishReadyToRun=true] [-p:PublishSingleFile=true] [-p:PublishTrimmed=true]
[-r|--runtime <RUNTIME_IDENTIFIER>] [--self-contained [true|false]]
[--no-self-contained] [-v|--verbosity <LEVEL>]
[--version-suffix <VERSION_SUFFIX>] dotnet publish -h|--help

dotnet publish 将编译应用程序,读取其在项目文件中指定的依赖项,然后将结果文件集发布到目录中。输出包括以下内容

  • 具有dll扩展名的程序集中的中间语言(IL)代码。
  • 一个.deps.json文件,其中包含项目的所有依赖项
  • 一个.runtimeconfig.json文件,它指定应用程序期望的共享运行时,以及该运行时的其他配置选项(例如,垃圾收集类型)。
  • 应用程序依赖的应用程序,从NuGet缓存目录复制到输出文件夹中。

从上述描述中,我们可以发现,通过dotnet publish指令,我们可以编译应用程序,生成并输出指定运行环境的交付物。

我们新建一个.NET 5的Console应用程序,同时引用Newtonsoft.Json Nuget包。

Main函数的代码:

using System;

namespace NET5PublishExample
{
class Program
{
static void Main(string[] args)
{
var msg = Newtonsoft.Json.JsonConvert.SerializeObject("Hello .NET 5!");
Console.WriteLine(msg);
Console.ReadKey();
}
}
}

首先,编译一下这个工程dotnet build,这一步很重要。然后,使用命令行执行dotnet publish指令:

我们看一下F:\GitHub\Source\Repos\NET5PublishExample\bin\Debug\net5.0\publish\目录下生成的文件:

正如上面所说,输出包括以下内容

  • 具有dll扩展名的程序集中的中间语言(IL)代码:NET5PublishExample.dll
  • 一个.deps.json文件,其中包含项目的所有依赖项NET5PublishExample.deps.json
  • 一个.runtimeconfig.json文件,它指定应用程序期望的共享运行时,以及该运行时的其他配置选项(例如,垃圾收集类型)。NET5PublishExample.runtimeconfig.json
  • 应用程序依赖的应用程序,从NuGet缓存目录复制到输出文件夹中。Newtonsoft.Json.dll

同时,还生成了一个Windows平台的可执行文件:NET5PublishExample.exe,双击可以执行:

另外,使用dotnet NET5PublishExample.dll,也可以直接执行:

然后有几个疑问:

NET5PublishExample.dll是不是可以跨平台运行?

在Linux、MacOS平台下有没有对应的可执行文件?

带着这2个问题,我们继续往下研究?

三、dotnet publish生成的可执行文件和跨平台二进制文件

自包含的方式发布应用程序,依赖于框架的方式发布应用程序。这两种发布模式默认情况下都会生成特定于平台的可执行文件和跨平台二进制文件。

1. 可执行文件

可执行文件不是跨平台的。它们特定于操作系统和CPU体系结构。因为Windows和linux下的可执行文件的结构和内容是不同的,所以可执行文件是分操作系统的。

创建可执行文件时,同时可以选择将应用程序发布为自包含或依赖于框架。

以自包含的方式发布应用程序包括该应用程序的.NET运行时,并且该应用程序的用户不必担心在运行该应用程序之前安装.NET。可以直接执行!

发布为与框架相关的应用程序不包括.NET运行时和库。仅包括应用程序和第三方依赖项。需要安装.NET 运行时。

这里我们示例2个平台的可执行文件:

① windows-x64平台

② Linux-x64平台

使用的dotnet publish指令 dotnet publish -r linux-x64 --self-contained false

生成的可执行文件:

2. 跨平台的二进制文件

将应用程序发布为依赖于框架的dll文件形式时,就会创建跨平台的二进制文件。该dll文件以项目命名。例如,如果您有一个名为应用程序NET5PublishExample,文件名为NET5PublishExample.dll创建。

以这种方式发布的应用程序dotnet <filename.dll>可以通过命令运行,可以在任何平台上运行。

四、关于自包含的发布选项和示例

以自包含的方式发布应用会生成特定于平台的可执行文件

输出发布文件夹包含应用程序的所有组件,包括.NET库和目标运行时。该应用程序与其他.NET应用程序隔离,并且不使用本地安装的.NET运行时。因此无需下载并安装.NET 运行时。

可执行二进制文件针对指定的目标平台生成。例如,如果您有一个名为NET5PublishExample的应用程序,并且发布了Windows的自包含可执行文件,则会创建NET5PublishExample.exe文件。对于Linux或macOS发布,将创建一个NET5PublishExample文件。目标平台和体系结构-r <RID>由dotnet publish命令的参数指定。有关RID的更多信息,请参见.NET RID目录

如果应用程序具有特定于平台的依赖项,例如包含特定于平台的依赖项的NuGet程序包,则这些依赖项将与应用程序一起复制到publish文件夹中。

这种模式的优势有哪些呢?

  1. 控制.NET版本:可以控制与应用程序一起部署的.NET版本。
  2. 指定运行的平台:因为必须为每个平台发布应用程序,所以我们需要确定应用程序将在何处运行。如果.NET引入了新平台,则必须先发布针对该平台的版本,然后才能在该平台上运行您的应用程序。

同时也带来了以下问题:

  1. 更大的部署内容:应用程序包括.NET运行时和所有应用程序依赖项,所以所需的下载大小和硬盘空间大于依赖于框架的版本。
  2. 难以更新.NET版本.NET Runtime:(随应用程序分发)只能通过发布新版本的应用程序进行升级。但是,.NET将根据应用程序运行的计算机上框架库的需要更新关键的安全补丁。

例如:

示例1:发布一个独立的应用程序,创建macOS 64位可执行文件,同时包含了.NET 运行时

dotnet publish -r osx-x64

生成的文件列表如下:包含macOS 64可执行文件NET5PublishExample,以及包含了对应macOS 64平台下的.NET 运行时

示例2:发布一个独立的应用程序,创建Windows 64位可执行文件,同时包含了.NET 运行时

 dotnet publish -r win-x64

生成的文件列表如下:包含Windows 64可执行文件NET5PublishExample.exe,以及包含了对应Windows 64平台下的.NET 运行时

五、关于依赖框架的发布选项和示例

  发布为依赖框架的应用程序是跨平台的,并且不包含.NET运行时。应用程序的运行需要单独安装指定版本的.NET运行时

应用程序的跨平台二进制文件可以使用dotnet <filename.dll>命令运行,并且可以在任何平台上运行。如果应用程序使用具有特定于平台的实现的NuGet包,则所有平台的依赖项都将与应用程序一起复制到publish文件夹中。

可以通过将-r <RID> --self-contained false参数传递给dotnet publish命令来为特定平台创建可执行文件。当-r参数被省略,为当前平台创建一个可执行文件。具有目标平台特定于平台的依赖关系的任何NuGet软件包都将复制到publish文件夹中。

这种模式带来的优势有:

  1. 小型部署:仅分发应用程序及其依赖项。.NET运行时和库由用户安装,所有应用共享运行时。
  2. 跨平台:应用程序和任何基于.NET的库都可以在其他操作系统上运行
  3.   使用最新的.NET运行时:该应用程序使用目标系统上安装的最新运行时(在.NET的目标主要,次要家族中)。这意味着应用程序会自动使用最新的.NET运行时修补版本。

同时也带来了以下问题:

  1. 需要预安装.NET 运行时仅在主机系统上已安装应用目标的.NET运行时版本时,应用才能运行。
  2. .NET可能会更改:.NET运行时和库可能会在运行该应用程序的计算机上更新。

例如:

示例1:发布一个当前平台的依赖框架的跨平台应用程序,不包含.NET 运行时,将与dll文件一起创建一个针对当前平台的可执行文件。

dotnet publish

使用dotnet NET5PublishExample.dll,可以直接运行(本机已经安装.NET运行时,NET5PublishExample.dll是跨平台的二进制文件

 示例2:发布一个依赖框架的跨平台应用程序(Linux 64位),不包含.NET 运行时,将创建一个Linux 64位可执行文件以及dll文件。

dotnet publish -r linux-x64 --self-contained false

使用dotnet NET5PublishExample.dll,可以直接运行(本机已经安装.NET运行时,NET5PublishExample.dll是跨平台的二进制文件

六、ReadyToRun编译选项

通过将应用程序程序集编译为ReadyToRun(R2R)格式,可以改善.NET应用程序的启动时间和延迟。R2R是一种提前(AOT)编译的形式。

R2R二进制文件通过减少应用程序加载时即时(JIT)编译器需要完成的工作量来提高启动性能。与JIT产生的代码相比,二进制文件包含相似的本机代码

但是,R2R二进制文件较大,因为它们既包含中间语言(IL)代码(某些情况下仍然需要此代码)和同一代码的本机版本。仅当发布针对特定运行时环境(RID)(例如Linux x64或Windows x64)的应用程序时,R2R才可用。

总结一下:通过R2R方式,可以直接将代码编译为Native Code,减少.NET 程序第一次加载时JIT编译带来的性能消耗,以提升.NET应用的首次加载性能。类似于ngen的程序集预加载。关于Ngen可以参考这个链接:ngen

对应的dotnet publish指令选项:   dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true

以上是.NET 5/.NET Core应用程序的发布部署的一些研究和分享。

推荐一个不错的知识链接:https://docs.microsoft.com/en-us/dotnet/core/deploying/#publish-framework-dependent?WT.mc_id=DT-MVP-5003918

周国庆

2020/2/15

快速搞懂.NET 5/.NET Core应用程序的发布部署的更多相关文章

  1. 一文教你快速搞懂速度曲线规划之S形曲线(超详细+图文+推导+附件代码)

    本文介绍了运动控制终的S曲线,通过matlab和C语言实现并进行仿真:本文篇幅较长,请自备茶水: 请帮忙点个赞

  2. [亲测]ASP.NET Core 2.0怎么发布/部署到Ubuntu Linux服务器并配置Nginx反向代理实现域名访问

    前言 ASP.NET Core 2.0 怎么发布到Ubuntu服务器?又如何在服务器上配置使用ASP.NET Core网站绑定到指定的域名,让外网用户可以访问呢? 步骤 第1步:准备工作 一台Liun ...

  3. [亲测]七步学会ASP.NET Core 2.0怎么发布/部署到Ubuntu Linux服务器并配置Nginx反向代理实现域名访问

    前言 ASP.NET Core 2.0 怎么发布到Ubuntu服务器?又如何在服务器上配置使用ASP.NET Core网站绑定到指定的域名,让外网用户可以访问呢? 步骤 第1步:准备工作 一台Liun ...

  4. vs2017创建.net core 应用程序,发布到Linux

    1.打开vs2017,创建.net core 应用程序 压缩上传到linux

  5. [笔记] .net core WPF 程序,发布独立程序与单一执行程序

    如何发布独立可运行的,和只有一个文件的 .net core WPF 程序. 1 发布并打包 Step 1 在项目上右键,在菜单中选择发布,使用文件发布,选择独立部署模式,其它的条件,依照自己的情况设置 ...

  6. 一文带你快速搞懂动态字符串SDS,面试不再懵逼

    目录 redis源码分析系列文章 前言 API使用 embstr和raw的区别 SDSHdr的定义 SDS具体逻辑图 SDS的优势 更快速的获取字符串长度 数据安全,不会截断 SDS关键代码分析 获取 ...

  7. 一文教你快速搞懂速度曲线规划之T形曲线(超详细+图文+推导+附件代码)

    运动控制中常用的T速度曲线规划的原理和程序实现,最后给出了测试结果: 如果本文帮到了您,请帮忙点个赞

  8. 一篇文章快速搞懂Qt文件读写操作

    导读:Qt当中使用QFile类对文件进行读写操作,对文本文件也可以与QTextStream一起使用,这样读写操作会更加简便.QFileInfo可以用来获取文件的信息.QDir可以用于对文件夹进行操作. ...

  9. android基于口令加密快速搞懂(一)

    import java.util.Random; import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypt ...

随机推荐

  1. Zabbix监控虚拟机服务-告警与自动恢复-模板化

    上一篇文章测试了服务的告警与自动恢复:Zabbix监控虚拟机服务-告警与自动恢复 但是我是直接为某一个主机增加的监控项和触发器, 如果要让某一个自定义的监控项和触发器被很多机器共用,则需要创建模板 1 ...

  2. Py-时间,随机,os,sys,jsonpickle序列化,shelve,xml模块

    内置模块 1.时间模块 第一:time.time()是时间戳 时间戳默认是 从1970年到现在过的秒数,是一个很长的数值它可以做时间的计算以及显示 第二:localtime() 获取当前的时间,按元组 ...

  3. 07. struts2中对Action的管理方式

    web.xml配置文件的常用代码 <filter> <filter-name>struts2</filter-name> <filter-class>o ...

  4. 转 14 jmeter性能测试实战--数据库MySQL

    14 jmeter性能测试实战--数据库MySQL   需求 测试用户表(对用户表select操作) 测试步骤 1.MySQL驱动下载并安装. 2.测试计划面板点击"浏览"按钮,将 ...

  5. 使用pushplus+python实现亚马逊到货消息推送微信

    xbox series和ps5发售以来,国内黄牛价格一直居高不下.虽然海外amazon上ps5补货很少而且基本撑不过一分钟,但是xbox series系列明显要好抢很多. 日亚.德亚的xbox ser ...

  6. 苹果 M1 芯片 OpenSSL 性能测试

    Apple M1(MacBook Air 2020) type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes md2 0.00 0.00 0.00 ...

  7. 【LinxuShell】tar命令的用法

  8. Spring常见问题总结

    1. 什么是 Spring 框架? Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性.Spring 官网:https://spring.io/. 我们一般说 Sprin ...

  9. 如何手动封装Promise函数

    第一步:Promise构造函数接受一个函数作为参数,该函数的两个参数分别是:resolve和reject; function Promise(task) { // 缓存this let that = ...

  10. SSH框架搭建详细步骤整理

    学习Java面前有两座山,一座山叫SSM,一座山叫SSH,跨越了这两座山之后才能感受到这个语言的魅力所在,SSM框架的搭建详细在之前博客已经涉及了,今天来整理SSH框架详细步骤: 生有涯 而 学无涯 ...