理解ASP.NET 5運行時命令:DNVM, DNX, 和DNU
ASP.NET 5 引入了一個新型的運行時,讓我們可以現場交付模式組合式構建應用程序,而不依賴于宿主機上的.NET框架。這種新模式為我們提供了命令行工具(DNVM、 DNX、DNU)用于管理我們的.net 版本,依賴的庫和運行環境,我們可以不需要Visual Studio,只需要一個文本編輯器和命令行就可以開發一個應用程序。
了解.NET 版本管理器 (DNVM) 之間 ,.NET 執行環境 (DNX) 和.NET 開發實用程序 (DNU) 之間的關系是開發 ASP.NET 5的根本。在這篇文章我們將看看在CentOS安裝并使用 DNVM,DNX 和 DNU ,從命令行和文本編輯器開發一個簡單應用程序,如果你使用docker,這些命令和docker還真是很像。
DNVM ( .NET Version Manager ):由于要實現跨平臺的目錄,微軟提供了 DNVM 功能, DNVM 是 ASP.NET 最底層的內容,它是一組 Powershell 腳本,用于啟動指定版本的 ASP.NET 運行環境,并且可以在同一臺機器的同一時間點上通過使用 Nuget 工具來管理各種版本的 ASP.NET 運行環境( DNX ),以及進行相應的升級操作。
DNX ( .NET Execution Environment ): DNX 是 ASP.NET 程序的運行環境,用于啟動并運行 ASP.NET 程序。該運行環境包括了編譯系統、 SDK 工具集、 Native CLR 宿主環境。可以使用 DNVM 管理各種版本的 DNX ,如 dnvm list 命令可以列出所有可用的 DNX 環境,而 dnvm install 1.0.0-beta4 則可以將指定版本的 DNX 安裝到 .dnx 文件夾,你可以在 %USERPROFILE%\.dnx\runtimes 目錄下找到已安裝所有版本的 DNX 。不同的操作系統有不同的 DNX 版本。
dnx.exe :dnx.exe 是用于啟動自宿主環境( Self-Hosting )的命令行工具,在使用命令行代碼進行自宿主環境啟動程序時, dnx 負責查找并調用 CLR Native Host , dnx 命令是整個運行環境的入口點,你可以使用 dnx run 來啟動程序。
dnu ( DNX Utility ) : 是一個命令行的包管理器,包含在 DNX 內,所以只要安裝了 DNX ,就可以使用 dnu 命令, 其可以用于恢復程序包、安裝程序包、部署程序包等等,比如把 project.json 里自定義的程序集自動下載下來進行使用。
DNX 架構及運行原理
DNX 是 ASP.NET 程序運行的核心,其遵循如下兩個準則:
- DNX 應該是自包含的, DNX 在解析完應用程序依賴樹以后才能知道要使用哪個 Core CLR 包,所以在得到解析樹之前, DNX 是無法加載任何 CLR 的,但 Roslyn 編譯器除外。
- 依賴注入( Dependency Injection ,簡稱 DI )貫穿著整個系統棧, DI 是 DNX 的一個核心部分,所有 DNX 上的類庫都構建在 DI 之上。
DNX 執行環境的分層架構如下:
Layer 0:Native Process
該層的功能非常簡單,主要就是用于查找并調用 Layer 1 里的 CLR Native Host ,并將系統相關的參數傳遞給 native host ,以便后續使用。目前 Windows 下使用 DNX.exe 來處理這個事情,而 IIS 也提供了一個中介(網站 bin 目錄下提供一個 AspNet.Loader.dll )可以將請求轉發給 Native Host ;而 Linux 和 Mac 則通過其相應版本的 dnx 來支持這項功能。
DNX 用法:
dnx . exe -- lib {paths} -- appbase {path} [ ProgramName ]
--lib {paths} :程序集 dll 的保存地址(一般是引用的第三方程序集和項目預編譯程序集),該地址是 Layer 2 層的托管代碼入口點可以加載程序集的地方。
--appbase {path} :程序保存的目錄,默認為 %CD% 。
[ProgramName] :程序名稱,該程序所在的程序集(或者是含有 Programe::Main 的 dll )保存在 --lib 路徑下,默認值是 appbase\project.json 里的 name 。大多數情況下,該名稱都是包含著加載鏈的程序宿主( Microsoft.Net.ApplicationHost )。但是,如果你的程序包含了入口點( Main 方法),并被編譯到 --lib 目錄下的話,你就可以使用該程序集的名稱作為 [ProgramName] ,這種方式將完全忽略加載鏈并直接啟動你的程序。
Layer 1 : CLR Native Host
這一層的內容依賴于你所選擇呢 CLR 版本,該層有如下兩個職責:
- 啟動 CLR ,啟動哪個 CLR 取決于你選擇的 CLR 版本。如果是 Core CLR ,該層會加載 coreclr.dll ,配置并啟動運行環境,然后創建應用程序域( AppDomain ),以便運行所有的托管代碼。
- 調用托管代碼的入口點( Layer 2 ),一旦 Native Host 的入口點返回了該線程,就會把 CLR 的線程清理干凈并關閉,比如,卸載應用程序域( AppDomain )并停止運行環境。
Layer 2 : Managed Entry Point
Layer 2 層(托管代碼入口)是編寫托管代碼的第一層,其職責如下:
- 創建 LoaderContainer (其包含需要的 ILoaders ), ILoader 負責根據程序集的名稱來加載程序集。 CLR 需要一個程序集的話, LoaderContainer 就會使用其 ILoader 來解析所需要的程序集。
- 從 --lib 的路徑目錄下,用根 ILoader 來加載程序集,并解析其依賴。
- 調用程序的主入口點。
Layer 3 : Application host/Application
如果開發人員將整個程序編譯成程序集放在 libpath 目錄下,那該層就是你的應用程序了。使用的時候,將含有程序入口點的程序集名稱作為 [ProgramName] 的參數傳入即可, Layer 2 層會直接調用該程序集。
不過,一般其它情況下,都會使用一個應用程序宿主( Application host )來解析程序的依賴內容并啟動運行程序。 Microsoft.Net.ApplicationHost 是運行環境提供的應用程序宿主,并擁有如下職責:
- 解析 project.json 里定義的各種依賴程序集。
- 將一個 ILoader 添加到 LoaderContainer ,以便從各種地方(如源代碼、 NuGet 、 Roslyn 等)加載相應的程序集。
- 調用程序集的入口點,將其作為下一個參數,傳遞給 DNX.exe 。
Layer 4 : Application
這一層,就是開發人員開發的程序,其運行在應用程序宿主之上。
環境配置:
要對 ASP.NET 5 程序的運行環境 DNX 進行配置,首先需要安裝并配置 DNVM , CentOS 等 Linux 系統上需要先安裝 Mono ,可以參照文章 CentOS 7 上部署 Mono 4 和 Jexus 5.6 。然后運行下面命令
curl -sSL https://raw.githubusercontent. com /aspnet/Home/master/dnvminstall. sh | sh && source ~/.dnx/dnvm/dnvm. sh
[root@Mono ~]# curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh
Downloading dnvm as script to '/root/.dnx/dnvm'
Appending source string to /root/.bash_profile
Type 'source /root/.dnx/dnvm/dnvm.sh' to start using dnvm
運行命令 dnvm :
上述 DNVM 安裝以后,系統會將 dnvm 文件復制到 /root/.dnvm 目錄,并將 /root/.dnvm 目錄添加到環境變量中,以便全局都可以使用。注意:這里只是安裝了 DNVM ,并沒有安裝任何版本的 DNX ,要安裝 DNX 的話,可以通過運行 dnvm 或 dnvm help 來查找相關的命令,具體命令如下:
dnvm upgrade [-x86][-x64] [-svr50][-svrc50] [-g|-global] [-proxy < ADDRESS > ]
- 從 feed 源安裝最新版的 DNX
- 為已安裝的 DNX 設置一個默認( default )別名
- 將 DNX bin 添加的用戶 PATH 環境變量中
- -g|-global 在全局內進行安裝(其它用戶也可以使用)
- -f|-force 強制更新成最新版(即便最新版已經安裝過了)
- -proxy 訪問遠程服務器的時候使用特定的地址作為代理
dnvm install < semver > | < alias > | < nupkg > |latest [-x86][-x64] [-svr50][-svrc50] [-a|-alias < alias > ] [-g|-global] [-f|-force]
- | 從 feed 源安裝指定的 DNX
- 從本地文件系統安裝指定的 DNX
- latest 從 feed 源安裝最新版的 DNX
- 將 DNX bin 添加到當前命令行的 path 環境變量中
- -p|-persistent 將 DNX bin 添加到系統 PATH 環境變量中
- -a|-alias 對指定安裝的 DNX 設置別名
- -g|-global 在全局內進行安裝
- -f|-force 強制安裝指定的 DNX (即便該版本已經安裝過了)
dnvm use < semver > | < alias > |none [-x86][-x64] [-svr50][-svrc50] [-p|-persistent] [-g|-global]
- | 將 DNX bin 添加到當前命令行的 path 環境變量中
- none 將 DNX bin 從當前命令行的 path 環境變量中刪除
- -p|-persistent 將 DNX bin 添加到系統 PATH 環境變量中
- -g|-global 組合使用 -p 將用戶 PATH 修改成系統 PATH
dnvm list // 列出所有已安裝的 DNX 版本
dnvm alias // 列出所有定義了別名的 DNX 版本
dnvm alias < alias > // 顯示定義了別名的 DNX 名稱
dnvm alias < alias > < semver > [-x86][-x64] [-svr50][-svrc50] // 給指定的 DNX 版本設置別名
管理程序集的 dnu 命令和 feed 源配置
通過dnu命令進行包管理的時候,通常使用如下命令:
dnu restore:查詢程序的所有依賴包,并將其全部下載到packages目錄,該命令會下載整個依賴包以及這些依賴包所依賴的其它依賴包。
dun install <package id>:該install命令用于下載指定的程序包并添加到程序中。
dun publish:該命令會將你的程序打包到一個可以運行的自包含目錄中。其會創建如下目錄結構:
output/
output/packages
outpot/appName
output/commandName.cmd
packages目錄包含所有應用程序需要的程序包。
appName目錄包含所有應用程序的代碼,如果引用了其它項目,則在引用的其它項目也會創建各自項目的同級目錄,即生成的目錄會和AppName同級。
publish命令,會將project.json中的commands節點中的各種命令,分別生成響應的命令行文件,如commands里的web命令,我們就可以通過dnx web(格式:dnx <command>)開運行它。
由于dnu在內部使用了Nuget命令,進行程序包的管理,所以使用的時候要正確配置Nuget的feed源,目前ASP.NET 5相關的包都在myget feed上,所以我們需要添加這個feed才能正常運行。這些配置信息在*nix下Mono使用的~/.config/NuGet /NuGet.config文件中進行管理,示例如下:
從命令行開始構建一個控制臺程序
我們使用 vim 和 dnx/dnu 命令行構建一個簡單的程序,創建一個目錄 dnx_demo, 在目錄下創建一個 project.json 文件,包含下面內容:
{
"version": "1.0.0-*",
"description": "geffzhang demo project",
"commands": {
"runme": "dnx_demo"
},
"frameworks": {
"dnx451": { },
"dnxcore50": {
"dependencies": {
"System.Console": "4.0.0-beta-22816",
"Microsoft.CSharp": "4.0.0-beta-22816"
}
}
}
}
上面我們定義了一個命令"runme",它指向的是工程名稱:dnx_demo。我們可以通過命令行使用dnx運行我們的項目,我們的項目指向傳 統的.NET Framework (dnx451) 和 .NET Core (dnxcore50),所以我們可以用dnx和.net fx運行。
然后在創建一個 Program.cs文件,內容如下:
using System;
namespace dnx_demo
{
public class Program
{
public void Main(string[] args)
{
Console.WriteLine("No Visual Studio Here!!");
Console.Read();
}
}
}
保存后,我們先運行命令 dnu restore ,將我們依賴的程序包下載到 package 目錄。
目前我們運行的程序還僅僅是一個非常簡單的控制臺程序,還沒有包括EF, SignalR, Identity等復雜組件,但從整個部署過程中,我們可以感覺到其實差距已經很小. 首先運行和部署環境DNVM和dnu, dnx命令和VS 2015的環境是一致的,而且組件包都是從Nuget上獲取,這和標準的Windows開發環境并沒有太大區別。