1. 程式人生 > >進擊的.NET 在雲原生時代的蛻變

進擊的.NET 在雲原生時代的蛻變

你一定看過這篇文章 《進擊的 Java ,雲原生時代的蛻變》,  本篇文章的靈感來自於這篇文章。北京時間9.24 就將正式釋出.NET Core 3.0, 所以寫下這篇文章讓大家全面認識.NET Core。

.NET 生態系統是一個不斷變化的生態圈,我相信它正在朝著一個偉大的方向發展。正好 最近 InfoQ 上釋出了一篇文章《.NET 生態系統概覽》,有了開源和跨平臺這兩個關鍵優先事項,我們就可以放心了。 下面我們來參考文章《進擊的 Java ,雲原生時代的蛻變》對雲原生對應用執行時的不同需求,說明一個.NET Core 3.0 在雲原生時代所完成的蛻變:

  • 體積更小:對於微服務分散式架構而言,更小的體積意味著更少的下載頻寬,更快的分發下載速度,.NET Core 的映象體積都很小,alpine的映象更小,帶上應用程式通常80M。
  • 啟動速度更快:對於傳統單體應用,啟動速度與執行效率相比不是一個關鍵的指標。原因是,這些應用重啟和釋出頻率相對較低。然而對於需要快速迭代、水平擴充套件的微服務應用而言,更快的的啟動速度就意味著更高的交付效率,和更加快速的回滾。尤其當你需要釋出一個有數百個副本的應用時,緩慢的啟動速度就是時間殺手。對於Serverless 應用而言,端到端的冷啟動速度則更為關鍵,即使底層容器技術可以實現百毫秒資源就緒,如果應用無法在 500ms 內完成啟動,使用者就會感知到訪問延遲。這裡我拿AWS Lambda來舉例,因為各大雲廠商都是以AWS是模仿的目標,AWS Lambda中可用的所有語言都是高階的,而不是像Assembler,C / C ++或Objective C那樣。從指令碼語言到JavaScript和Python,再到像Java和C#到Go這樣被編譯為二進位制檔案的託管執行時的語言,所有語言都是他們有自己的長處。在基準測試中,最重要的.NET Core是 冠軍,具體參看https://react-etc.net/entry/aws-lambda-benchmarks-node-js-python-java-c-go-dotnet-core
  • 佔用資源更少:執行時更低的資源佔用,意味著更高的部署密度和更低的計算成本。.NET Core的 CLR啟動速度非常快,降低啟動時資源消耗,可以減少資源爭搶,更好保障其他應用 SLA。
  • 支援水平擴充套件:.NET Core 3.0預設更好的支援Docker資源限制,官方團隊也在努力讓.NET Core成為真正的容器執行時,使其在低記憶體環境中具有容器感知功能並高效執行。 具體可以參看文章《從CLR GC到CoreCLR GC看.NET Core對雲原生的支援》,隨著記憶體成本的下降和虛擬化的流行,大記憶體配比已經成為趨勢。所以我們一般是採用水平擴充套件的方式,同時部署多個應用副本,在一個計算節點中可能執行一個應用的多個副本來提升資源利用率。

.NET Core 3.0 新增功能 大部分的功能特性已經公開,可以通過網頁:https://docs.microsoft.com/zh-cn/dotnet/core/whats-new/dotnet-core-3-0,其中有幾個特性是非常期待應用到生產的,很多人都在等待著明天的正式釋出。

預設可執行檔案

.NET Core 現在預設生成依賴於框架的可執行檔案,這個行為是和.NET Framework保持一致了。 對於使用全域性安裝的 .NET Core 版本的應用程式而言,這是一種新行為。 以前,僅獨立部署會生成可執行檔案。

dotnet builddotnet publish 期間,將建立一個與你使用的 SDK 的環境和平臺相匹配的可執行檔案。 和其他本機可執行檔案一樣,可以使用這些可執行檔案執行相同操作,例如:

  • 可以雙擊可執行檔案。
  • 可以直接從命令提示符啟用應用程式,如 Windows 上的 myapp.exe
    ,以及 Linux 和 macOS 上的 ./myapp

單檔案可執行檔案

dotnet publish 命令支援將應用打包為特定於平臺的單檔案可執行檔案。 該可執行檔案是自解壓縮檔案,包含執行應用所需的所有依賴項(包括本機依賴項)。 首次執行應用時,應用程式將根據應用名稱和生成識別符號自解壓縮到一個目錄中。 再次執行應用程式時,啟動速度將變快。 除非使用了新版本,否則應用程式無需再次進行自解壓縮。

若要釋出單檔案可執行檔案,請使用 dotnet publish 命令在專案或命令列中設定 PublishSingleFile

<PropertyGroup>
   <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
   <PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>

或者

dotnet publish -r win10-x64 /p:PublishSingleFile=true

這個單檔案是大家一直期待的go 特性,.NET Core 3.0正式有了,更詳細的資訊參看 https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md

程式集連結

.NET core 3.0 SDK 隨附了一種工具,可以通過分析 IL 並剪裁未使用的程式集來減小應用的大小。

自包含應用包括執行程式碼所需的所有內容,而無需在主計算機上安裝 .NET。 但是,很多時候應用只需要一小部分框架即可執行,並且可以刪除其他未使用的庫。

.NET Core 現在包含一個設定,將使用 IL 連結器工具掃描應用的 IL。 此工具將檢測哪些程式碼是必需的,然後剪裁未使用的庫。 此工具可以顯著減少某些應用的部署大小。

要啟用此工具,請使用專案中的 <PublishTrimmed> 設定併發布自包含應用:

<PropertyGroup>
   <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

或者

dotnet publish -r <rid> -c Release

例如,包含的基本“hello world”新控制檯專案模板在釋出時命中大小約為 70 MB。 通過使用 <PublishTrimmed>,其大小將減少到約 30 MB,這個特性可以用於進一步的減小應用程式的大小。請務必考慮到使用反射或相關動態功能的應用程式或框架(包括 ASP.NET Core 和 WPF)通常會在剪裁時損壞。

分層編譯

.NET Core 3.0 中預設啟用了分層編譯 (TC)。 此功能使執行時能夠更適應地使用實時 (JIT) 編譯器來獲得更好的效能,這也是一個可以加速應用啟動的選項。

TC 的主要優勢是使(重新)實時編譯方法能夠要麼犧牲程式碼質量以更快地生成程式碼,要麼以較慢的速度生成更高質量的程式碼。 這有助於提高應用程式在從啟動到穩定狀態的各個執行階段的效能。 這與非 TC 方法完全不同,其中每種方法均以單一方式進行編譯(與高質量層相同),這種方法偏向於穩定狀態而不是啟動效能。

若要啟用快速 JIT(第 0 層實時編譯的程式碼),請在專案檔案中使用此設定:

<PropertyGroup>
   <TieredCompilationQuickJit>true</TieredCompilationQuickJit>
</PropertyGroup>

ReadyToRun 映像

可以通過將應用程式集編譯為 ReadyToRun (R2R) 格式來改進.NET Core 應用程式的啟動時間。 R2R 是一種預先 (AOT) 編譯形式,這也是一項加速應用啟動時間的選項。

R2R 二進位制檔案通過減少應用程式載入時實時 (JIT) 編譯器需要執行的工作量來改進啟動效能。 二進位制檔案包含與 JIT 將生成的內容類似的本機程式碼。 但是,R2R 二進位制檔案更大,因為它們包含中間語言 (IL) 程式碼(某些情況下仍需要此程式碼)和相同程式碼的本機版本。僅當釋出面向特定執行時環境 (RID)(如 Linux x64 或 Windows x64)的自包含應用時 R2R 才可用。

主要版本前滾

.NET Core 3.0 引入了一項選擇加入功能,該功能允許應用前滾到 .NET Core 最新的主要版本。 此外,還添加了一項新設定來控制如何將前滾應用於應用。 這可以通過以下方式配置:

  • 專案檔案屬性:RollForward
  • 執行時配置檔案屬性:rollForward
  • 環境變數:DOTNET_ROLL_FORWARD
  • 命令列引數:--roll-forward

必須指定以下值之一。 如果省略該設定,則預設值為“Minor” 。

  • LatestPatch
    前滾到最高補丁版本。 這會禁用次要版本前滾。
  • Minor
    如果缺少所請求的次要版本,則前滾到最低的較高次要版本。 如果存在所請求的次要版本,則使用 LatestPatch 策略。
  • Major
    如果缺少所請求的主要版本,則前滾到最低的較高主要版本和最低的次要版本。 如果存在所請求的主要版本,則使用 Minor 策略。
  • LatestMinor
    即使存在所請求的次要版本,仍前滾到最高次要版本。 適用於元件託管方案。
  • LatestMajor
    即使存在所請求的主要版本,仍前滾到最高主要版本和最高次要版本。 適用於元件託管方案。
  • Disable
    不前滾。 僅繫結到指定的版本。 建議不要將此策略用於一般用途,因為它會禁用前滾到最新補丁的功能。 該值僅建議用於測試。

Docker 和 cgroup 記憶體限制

從預覽版 3 開始,在 Linux 上使用 Docker 執行 .NET Core 3.0 時,可以更好地處理 cgroup 記憶體限制。 執行具有記憶體限制的 Docker 容器(例如使用 docker run -m)會更改 .NET Core 的行為方式。

  • 預設垃圾回收器 (GC) 堆大小:最大為 20 MB 或容器記憶體限制的 75%。
  • 可以將顯式大小設定為絕對數或 cgroup 限制的百分比。
  • 每個 GC 堆的最小保留段大小為 16 MB。 此大小可減少在計算機上建立的堆數量。

垃圾回收堆大小減小

垃圾回收器的預設堆大小已減小,以使 .NET Core 使用更少的記憶體。 此更改更符合具有現代處理器快取大小的第 0 代分配預算。

垃圾回收大型頁面支援

大型頁面(也稱為 Linux 上的巨型頁面)是一項功能,其中作業系統能夠建立大於本機頁面大小(通常為 4K)的記憶體區域,以提高請求這些大型頁面的應用程式的效能。

現在可以使用 GCLargePages 設定將垃圾回收器配置為一項選擇加入功能,以選擇在 Windows 上分配大型頁面。


.NET 技術在雲原生時代也在不停地進化。.NET Core 作為.NET 生態的非常重要的一員,在對現有 .NET 應用保持高度相容的同時,對啟動速度和記憶體佔用做了細緻的優化,比較適於微服務架構配合使用, 在以kubernetes 為代表的雲原生應用開發平臺上發生蛻變。

在雲原生時代,我們要能夠在橫向的應用開發生命週期中,將開發、交付、運維過程進行有效的分割和重組,提升研發協同效率;並且要能在整個縱向軟體技術棧中,在程式設計模型、應用執行時和基礎設施等多層面進行系統優化,實現 radical simplification,提升系統效率。