主頁 > .NET開發 > .NET5.0 單檔案發布打包操作深度剖析

.NET5.0 單檔案發布打包操作深度剖析

2020-09-09 22:44:16 .NET開發

.NET5.0 單檔案發布打包操作深度剖析

前言

隨著 .NET5.0 Preview 8 的發布,許多新功能正在被社區成員一一探索;這其中就包含了“單檔案發布”這個炫酷的功能,實際上,這也是社區一直以來的呼聲,從 WinForm 的 msi 開始,我們就希望有這樣一個功能,雖然在 docker 時代,單檔案發布的功能顯得“不那么重要”,但正是從這一點可以看出,.NET 的團隊成員一直在致力于實用功能的完善,

在 Java 的世界里,單檔案發布一直伴隨著他們的成長,War 檔案可以直接上傳到 Tomcat 上運行,話說我們還是有那么一丟丟的羨慕的,不過凡事有利就有弊,單檔案發布對于細分模塊的熱更新來說,還有有一點點的不方便,

不過瑕不掩瑜,在微服務概念越來越火熱的今天,相信單檔案發布的功能帶給大家更多的是興奮,

什么是單檔案發布

首先,我們要清楚的了解,什么是單檔案發布,

官方的目標定義:

.Net 5.0單個檔案解決方案應為:

  • 廣泛兼容:可以將包含IL程式集,隨時運行的程式集,復合程式集,本機二進制檔案,組態檔等的應用程式打包為一個可執行檔案,
  • 可以直接從打包軟體直接運行應用程式的托管組件,而無需提取到磁盤,
  • 可與除錯器和工具一起使用,

從上面的目標可以看出,和以往版本最大的不同在于:將所有依賴打包到一個可執行檔案中,可直接運行,不影響除錯操作,

注意上面的這句話“將所有依賴打包到一個可執行檔案中”,而在以往,我們使用 dotnet publish 將應用程式進行發布之后,我們會看到,在 publish 下有許多專案依賴的 dll 檔案,在 .NET5.0 到來之后,這些依賴檔案可收納到一個檔案中,瞬間讓人感受到了清涼,

發布操作指令相關

命令

平臺 命令 說明
Linux dotnet publish -r linux-x64 /p:PublishSingleFile=true -
Windows dotnet publish -r win-x64 --self-contained=false /p:PublishSingleFile=true -
Mac OS - -

可選引數

屬性 描述
IncludeNativeLibrariesInSingleFile 在發布時,將依賴的本機二進制檔案打包到單檔案應用程式中,
IncludeSymbolsInSingleFile 將 .pdb 檔案打包到單個檔案中,提供該選項是為了和 .NET 3 單檔案模式兼容,建議替代的方法是生成帶有嵌入式的 PDB (embedded)的程式集
IncludeAllContentInSingleFile 將所有發布的檔案(符號檔案除外)打包到單檔案中,該選項提供是為了向后兼容 .NETCore 3.x 版本

組態檔設定引數

除了可以使用命令列引數的形式,還可以通過組態檔的形式設定發布引數,編輯專案檔案,添加配置節點到檔案中并保存即可,

<PropertyGroup>
  <TargetFramework>net5.0</TargetFramework>
  <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
  <PublishSingleFile>true</PublishSingleFile>
  <IncludeContentInSingleFile>true</IncludeContentInSingleFile>
</PropertyGroup>

關于 RID 說明見:https://docs.microsoft.com/en-us/dotnet/core/rid-catalog

這是截止本文發布前的 RID 版本,不排除 .NET5.0 有新的發布

其它引數

除了上面的三個可選引數,我在查詢檔案的程序中還發現,官方還提到了其它引數的使用,目前不確定是否有效

<PropertyGroup>
  <SelfContained>true</SelfContained>
  <!--啟用使用assemby修剪-僅支持自包含應用程式-->
  <PublishTrimmed> true </PublishTrimmed>
  <!--啟用AOT編譯 目前暫不支持預編譯-->
  <!--<PublishReadyToRun>true</PublishReadyToRun>-->
</PropertyGroup>
<ItemGroup>
  <Content Update="*-exclute.dll">
    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
  </Content>
</ItemGroup>

還可以通過設定 ExcludeFromSingleFile 元素,該設定將指定某些檔案不嵌入單個檔案之中,

撰寫待打包的應用程式

為了更直觀的看出正常發布和單檔案發布的區別,我們特別準備了一個 Web 應用程式,并對兩個程式集進行依賴參考,

準備好專案,編譯成功,嘗試發布,打開 PowerShel 控制臺,分別輸入以下命令

dotnet publish -r linux-x64 /p:PublishSingleFile=true
dotnet publish -r win-x64 --self-contained=false /p:PublishSingleFile=true

linux-x64 和 win-x64 兩個目錄下,分別有 publish 目錄,由于平臺的不同,所參考的依賴也不一樣,這是我們早就了解過的,我們看看打包前后的區別

以上執行的兩條命令陳述句,會為我們生成 Linux 和 Windows 兩個平臺的程式包,從上圖中可以看出,在打包之前,專案的各種參考依賴都被復制到了發布目錄下,這也是我們之前的程式發布方式,在經過打包后,所有依賴檔案都被裝入了一個可執行檔案中,在 Linux 平臺下表現為:PreviewWebApplication ,Windows 平臺下則為:PreviewWebApplication.exe,從打包效果來看,遷移將變得更加方便了,

運行打包程式

打包后的程式和未打包的發布程式在運行方式上沒有太多的差異性,在 Windows 平臺上,只需要雙擊 PreviewWebApplication.exe 就可以運行該打包程式了,本示例創建的是一個 WebApi 的程式,直接訪問程式偵聽的地址后得到介面回傳的結果,如果您創建的是帶有 Razor 視圖或者攜帶其它資源檔案的,可能無法訪問指定的 url,

在程式成功運行起來后,我們發現,打包程式并沒有解壓縮檔案到磁盤,而是直接從包中加載檔案到記憶體中運行;這是巨大的進步,也是和 War 檔案根本的區別,

需要注意的是,該 .exe 檔案并不能單獨復制到別的地方運行,你必須把 .exe 當前目錄完整的復制才能運行,這涉及到主機探測的問題,下面我們將會一一提到,

跨平臺的打包檔案

通過上面的示例我們了解到,打包程式總是為不同的平臺生成獨立的包程式,這是為什么呢?這里就涉及到一個概念,也就是 Tool Interface Standard (TIS)

Executable and Linking Format(ELF)

Common Object File Format(COFF)于1983年引入,最初使用在 AT&T 的 UNIX 系統上,由于 COFF 的各種局限性,比如:節的最大數量受到限制,節名稱,所包含的源檔案的長度受到限制,并且符號除錯資訊無法支持實際的語言,最后,在 System V Release 4 (SVR4) 發布后,AT&T 使用 ELF 替代了 COFF,

工具介面標準委員會
援引委員會規范檔案的說明:可執行檔案和鏈接格式最初由 UNIX 系統開發和發布實驗室(USL)作為應用程式二進制介面(API)的一部分,工具介面標準委員會 (TIS) 選擇將不斷發展的 ELF 標準作為便攜式物件檔案,該標準適用于各種作業系統的 32 位英特爾架構環境的格式,ELF 標準旨在通過向開發人員提供具有一組跨多個操作環境的二進制介面定義,這將減少不同介面實作的數量,從而減少需要重新撰寫和編譯的代碼,

ELF 檔案結構又分為三種型別,分別是:

名稱 說明 描述
可重定位檔案 Relocatable File 包含適合與其他物件檔案鏈接的代碼和資料,以創建可執行檔案或共享物件檔案,
可執行檔案 Executable File 包含適合執行的程式
共享目標檔案 Shared Object File 包含適合在兩種背景關系中鏈接的代碼和資料,首先,鏈接編輯器可以處理它與其他可重新洗掉和共享的物件檔案,以創建另一個物件檔案,其次,動態聯結器將其與可執行檔案和其他共享物件相結合,以創建行程映像,

Portable Executable (PE)

在 Windows 陣營,微軟在此 COFF 標準的基礎上,又進行了創新和發展出了 PE 檔案標準

PE Format
該規范描述了Windows作業系統家族下的可執行檔案(影像)和目標檔案的結構,這些檔案分別稱為可移植可執行(PE)和公用物件檔案格式(COFF)檔案,

從上面的兩種規范中可以看出,LinuX 和 Windows 都有各自的檔案格式規范,而這種規范在一定程度上是不兼容的,不論是從檔案結構還是決議方式;所以 .NET5.0 中的打包程式必須為不同的平臺實作獨立的打包器,打包器的實作在 runtime 中的 Microsoft.NET.HostModel 庫中,

認識了 ELF 和 PE 檔案結構之后,我們就可以對打包器代碼進行閱讀理解,

Microsoft.NET.HostModel

你可以從 github 上下載 .NET 5.0 的源代碼,
轉到目錄:

runtime/src/installer/managed/Microsoft.NET.HostModel

原始碼不太多,可直接進行閱讀,主要理解層次關系即可,

打包器主要包含了三大部分的內容,分別是 AppHost、Bundler、ComHost

模塊 說明
AppHost 用于單檔案主機啟動時的檔案探測,還復制將程式資源從 App.dll 復制到 AppHost備用,目前已通過 HostFxr 和 HostPolicy 進行靜態鏈接,其探測邏輯已轉移到 HostPolicy(由C++撰寫)
Bundler 打包器的具體實作,主要是將應用程式及其依賴項嵌入 AppHost 中,隨后發布單個可執行檔案到指定目錄
ComHost 創建一個包含嵌入式 CLSIDMap 檔案的 ComHost,以將 CLSID 映射到 .NET 類,

在檔案 Bundle/Manifest.cs 的頭部,我們看到了“單檔案程式”的檔案結構定義

 BundleManifest is a description of the contents of a bundle file.
 This class handles creation and consumption of bundle-manifests.
 
 Here is the description of the Bundle Layout:
 _______________________________________________
 AppHost 


------------Embedded Files ---------------------
The embedded files including the app, its
configuration files, dependencies, and 
possibly the runtime.







------------ Bundle Header -------------
    MajorVersion
    MinorVersion
    NumEmbeddedFiles
    ExtractionID
    DepsJson Location [Version 2+]
       Offset
       Size
    RuntimeConfigJson Location [Version 2+]
       Offset
       Size
    Flags [Version 2+]
- - - - - - Manifest Entries - - - - - - - - - - -
    Series of FileEntries (for each embedded file)
    [File Type, Name, Offset, Size information]
    
    

_________________________________________________

從上面的檔案結構中,我們可以非常清晰的看到,單檔案程式的結構一共分為三大部分,分別是:

定義 說明 描述
嵌入的檔案 Embedded file 主要是組態檔和描述檔案,比如 .deps.json,runtimeconfig.json 等檔案
打包檔案頭資訊 Bundle Header 描述了整個檔案的結構資訊,型別,存盤位置,段、表等資訊
物體清單 Manifest Entries 實際打包的檔案串列,每個檔案分段寫入,可執行檔案使用 16byte - prev file end position 進行分隔,普通檔案直接按 prev file end position 進行寫入

檔案頭資訊的查看

我們可以通過一些工具去查看已經打包好的檔案,在 Linux 下,可以使用 readelf/objdump 等程式來獲取 PreviewWebApplication 檔案的資訊,在 Windows 下,可以使用 PE Tools 等工具

Linux 下 readelf 讀取檔案頭資訊

從圖中我們可以看到 Type:DYN (Shared object file) 這是一個標準的共享物件檔案,關于 ELF 頭部資訊的內容不再展開,有興趣的同學可以自行學習相關內容,

Windows下 PE Tools 讀取檔案頭資訊

已經打包好的程式內部包含了 319(Linux)、Windows(359) 個檔案,Windows 版本在未打包前是 84.3MB,打包后是 69.8MB,最重要的是在運行時無需解壓縮,直接從 Bundle 中運行檔案,

檔案中的第三部分,也就是 “物體清單(Manifest Entries)的寫入代碼在 Bundle\Bundler.cs\AddToBundle

long AddToBundle(Stream bundle, Stream file, FileType type)
{
    if (type == FileType.Assembly)
    {
        long misalignment = (bundle.Position % AssemblyAlignment);
        if (misalignment != 0)
        {
            long padding = AssemblyAlignment - misalignment;
            bundle.Position += padding;
        }
    }
    file.Position = 0;
    long startOffset = bundle.Position;
    file.CopyTo(bundle);
    return startOffset;
}

在成員方法 GenerateBundle(IReadOnlyList fileSpecs) 內部迭代呼叫了 AddToBundle 方法,完成了物體清單檔案的寫入,

// 代碼片段

public string GenerateBundle(IReadOnlyList<FileSpec> fileSpecs)
{
  ...
 foreach (var fileSpec in fileSpecs)
  {
      string relativePath = fileSpec.BundleRelativePath;
      ...
      using (FileStream file = File.OpenRead(fileSpec.SourcePath))
      {
          FileType targetType = Target.TargetSpecificFileType(type);
          long startOffset = AddToBundle(bundle, file, targetType);
          FileEntry entry = BundleManifest.AddEntry(targetType, relativePath, startOffset, file.Length);
          Tracer.Log($"Embed: {entry}");
      }
  }

  // Write the bundle manifest
  headerOffset = BundleManifest.Write(writer);
  ...
}

因為解壓器的實作已經轉移到了 HostFxr 和 HostPolicy 中,以靜態鏈接庫的方式鏈接到打包器中,且該部分代碼由 C++ 進行撰寫,鑒于 C++ 水平有限,在這里不作介紹,

結束語

撰寫這篇文章耗費了我大量的時間,期間大量閱讀海量的參考資料、文獻、標準檔案、制作文章配圖等等,寫干貨文章真的需要投入巨大的精力和時間,希望你們喜歡,

文章進行到這里,我知道肯定還有很多同學沒看過癮,但是我們可以通過回顧打包器的開發進度表來體驗一下 .NET 團隊的開發熱情,

主要參考資料

.NET團隊計劃經理 Richard Lander 的博客:https://devblogs.microsoft.com/dotnet/announcing-net-5-0-preview-8/
Bundler 進度表:https://github.com/dotnet/runtime/issues/36590
single-file:https://github.com/dotnet/designs/tree/master/accepted/2020/single-file
ELF檔案:https://refspecs.linuxbase.org/elf/elf.pdf
ELF維基百科:https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
Readelf:https://sourceware.org/binutils/docs/binutils/readelf.html
PE檔案:https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
PE Tools:https://github.com/petoolse/petools

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/43.html

標籤:.NET Core

上一篇:基于 abp vNext 的快速開發模板

下一篇:.NET Core 中匯入匯出Excel

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more