.NET 6 Preview 3 中 ASP.NET Core 的更新和改進
原文:bit.ly/2Qb56NP
作者:Daniel Roth
譯者:精緻碼農-王亮
.NET 6 預覽版 3 現已推出,其中包括許多對新的 ASP.NET Core 改進。以下是本次預覽版的新內容:
- 更小的 SignalR、Blazor Server、MessagePack 腳本文件
- 啟用 Redis 分析會話
- HTTP/3 端點 TLS 配置
- 初步 .NET 熱重載支持
- Razor 編譯器不再生成單獨的視圖 Assembly
- IIS 中的淺拷貝支持
- 適用於 SignalR C++ 客戶端的 Vcpkg 端口
- 減少閑置 TLS 連接的內存佔用量
- 從 SlabMemoryPool 中移除板塊
- 用於 WPF 和 Windows 窗體的 BlazorWebView 控件
開始
要在.NET 6 Preview 3 中開始使用 ASP.NET Core,請安裝 .NET 6 SDK[1]。
如果你在 Windows 上使用 Visual Studio,我們建議安裝 Visual Studio 2019 16.10 的最新預覽版。如果你在 macOS 上,我們建議安裝 Visual Studio 2019 for Mac 8.10 的最新預覽版。
升級現有項目
要將現有的 ASP.NET Core 應用程序從 .NET 6 Preview 2 升級到 .NET 6 Preview 3:
- 更新所有
Microsoft.AspNetCore.*
包引用至6.0.0-preview.3.*
。 - 更新所有
Microsoft.Extensions.*
包引用至6.0.0-preview.3.*
。
查看 ASP.NET Core for .NET 6 中的完整中斷性更改列表[2]。
更小的腳本文件
得益於 Ben Adams 的社區貢獻,SignalR、MessagePack 和 Blazor Server 腳本現在明顯變小了,下載體積減少,瀏覽器解析和編譯 JavaScript 的次數減少,啟動速度加快。
這項工作帶來的下載體積減少是非常驚人的:
Library | Before | After | %↓ | .br |
---|---|---|---|---|
signalr.min.js | 130 KB | 39 KB | 70% | 10 KB |
blazor.server.js | 212 KB | 116 KB | 45% | 28 KB |
現在你也只需要為 MessagePack 提供@microsoft/signalr-protocol-msgpack
包,而不需要包含 msgpack5。這意味着你只需要額外的 29 KB 而不是之前的 140 KB 來使用 MessagePack 而不是 JSON。
下面說下我們是如何減少體積的:
- 更新 TypeScript 和依賴關係到最新版本.
- 將 uglify-js 換成了 terser,這是 webpack 的默認版本,支持新的 JavaScript 語言特性(比如
class
)。 - 將 SignalR 模塊標記為
"sideEffects":false
,這樣 tree-shaking 就更有效了。 - 丟棄了
"es6-promise/dist/es6-promise.auto.js"
的多邊填充。 - 更改 TypeScript 為輸出
es2019
而不是es5
,並放棄了es2015.promise
和es2015.iterable
的 polyfill。 - 從
@msgpack/msgpack
移到msgpack5
,因為它需要更少的 polyfills,並且是 TypeScript 和模塊感知的。
你可以在 GitHub 上 Ben 的 PR[3] 中找到更多關於這些變化的細節。
啟用 Redis 分析會話
我們接受了 Gabriel Lucaci 的社區貢獻,在此預覽版中使用Microsoft.Extensions.Caching.StackExchangeRedis
啟用 Redis 分析會話。關於 Redis 分析的更多細節,請參見官方文檔[4]。該 API 的使用方法如下:
services.AddStackExchangeRedisCache(options =>
{
options.ProfilingSession = () => new ProfilingSession();
})
HTTP/3 端點 TLS 配置
HTTP/3 與現有的 HTTP 協議相比具有許多優勢,包括更快的連接設置,以及在低質量網絡上的性能改進。
在此預覽版中,新增了使用UseHttps
在單個 HTTP/3 端口上配置 TLS 證書的功能。這使得 Kestrel 的 HTTP/3 端點配置與 HTTP/1.1 和 HTTP/2 一致。
.ConfigureKestrel((context, options) =>
{
options.EnableAltSvc = true;
options.Listen(IPAddress.Any, 5001, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http3;
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.ServerCertificate = LoadCertificate();
});
});
})
初步 .NET 熱重載支持
現在,使用dotnet watch
的 ASP.NET Core 和 Blazor 項目可以獲得對 .NET 熱重載的早期支持。.NET 熱重載可以在不重新啟動應用程序和不丟失應用程序狀態的情況下將代碼更改應用到你正在運行的應用程序中。
要在現有的基於 .NET 6 的 ASP.NET Core 項目中試用熱重載,請將"hotReloadProfile": "aspnetcore"
屬性添加到你的launchSettings.json
文件中。對於 Blazor WebAssembly 項目,使用"blazorwasm"
熱重載配置文件。
使用dotnet watch
運行項目。下面的輸出表明熱重載已經啟用:
watch : Hot reload enabled. For a list of supported edits, see //aka.ms/dotnet/hot-reload. Press "Ctrl + R" to restart.
在任何時候你想強制應用程序重新構建和重啟,你可以在控制台輸入Ctrl+R
來實現。
現在你可以開始對你的代碼進行編輯了。當你保存代碼更改時,相應的更改幾乎會在瞬間自動熱重載到運行中的應用程序中。運行中的應用程序中的任何狀態都會被保留。
你也可以對你的 CSS 文件進行熱重載更改,而不需要刷新瀏覽器:
有一些代碼更改不支持 .NET 執重載。你可以在文檔[5]中找到支持的代碼編輯列表。在 Blazor WebAssembly 中,目前只支持方法體替換。我們正在努力擴展 .NET 6 中支持的編輯集。當dotnet watch
檢測到無法使用熱重載應用的更改時,它就會退回重新構建和重新啟動應用程序。
這只是 .NET 6 中熱重載支持的開始。桌面和移動應用程序的熱重載支持將很快在即將到來的預覽版中提供,以及在 Visual Studio 中集成熱重載。
IIS 中的淺拷貝支持
我們在 IIS 的 ASP.NET Core 模塊中添加了一個新功能,以增加對淺拷貝應用程序程序集的支持。目前,.NET 在 Windows 上運行時鎖定了應用程序的二進制文件,使得在應用程序仍在運行時無法替換二進制文件。雖然我們的建議仍然是使用應用程序脫機文件,但我們認識到在某些情況下(例如 FTP 部署)不可能這樣做。
在這種情況下,你可以通過自定義 ASP.NET Core 模塊處理程序設置來啟用淺拷貝。在大多數情況下,ASP.NET Core 應用程序的web.config
不在源代碼版本控制中,你可以修改它(它們通常是由 SDK 生成的)。你可以添加這個web.config
示例來開始。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- To customize the asp.net core module uncomment and edit the following section.
For more info see //go.microsoft.com/fwlink/?linkid=838655 -->
<system.webServer>
<handlers>
<remove name="aspNetCore"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModulev2" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
<handlerSettings>
<handlerSetting name="experimentalEnableShadowCopy" value="true" />
<handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
<!-- Only enable handler logging if you encounter issues-->
<!--<handlerSetting name="debugFile" value=".\logs\aspnetcore-debug.log" />-->
<!--<handlerSetting name="debugLevel" value="FILE,TRACE" />-->
</handlerSettings>
</aspNetCore>
</system.webServer>
</configuration>
你需要一個新版本的 ASP.NET Core 模塊來嘗試這個功能。在自託管的 IIS 服務器上,這需要新版本的託管捆綁包。在 Azure App Services 上,你需要安裝新的 ASP.NET Core 站點運行時擴展。
適用於 SignalR C++ 客戶端的 Vcpkg 端口
Vcpkg 是一個跨平台的 C 和 C++庫的命令行包管理器。最近,我們為 vcpkg 添加了一個移植版本,為 SignalR C++ 客戶端添加了 CMake 本地支持(也適用於 MSBuild 項目)。
你可以用下面的代碼來添加 SignalR 客戶端到你的 CMake 項目中(假設你已經包含了 vcpkg 工具鏈文件)。
find_package(microsoft-signalr CONFIG REQUIRED)
link_libraries(microsoft-signalr::microsoft-signalr)
在這之後,SignalR C++ 客戶端就可以被#include
並用於你的項目中,而不需要任何額外的配置。這個倉庫[6]是一個完整的使用 SignalR C++ 客戶端的 C++ 應用程序的例子。
減少閑置 TLS 連接的內存佔用量
對於只偶爾來回發送數據的 TLS 長連接,我們已經大大減少了 .NET 6 中 ASP.NET Core 應用程序的內存佔用。這應該有助於提高 WebSocket 服務器等場景的可擴展性。這得益於System.IO.Pipelines
、SslStream
和Kestrel
的眾多改進。讓我們來看看促成這一方案的一些改進。
縮減 System.IO.Pipelines.Pipe 大小
對於我們建立的每一個連接,我們都會在 Kestrel 中分配兩個管道:一個是從傳輸層到應用的請求,另一個是從應用層到傳輸的響應。通過將System.IO.Pipelines.Pipe
的大小從 368 位元組縮減到 264 位元組(約 28.2%),我們為每個連接節省了 208 位元組(每個 Pipe 節省 104 位元組)。
SocketSender 池
SocketSender 對象在運行時約為 350 位元組。與其為每個連接分配一個新的 SocketSender 對象,我們可以將它們集中起來,因為發送通常非常快,我們可以減少每個連接的開銷。現在,我們不再為每個連接分配 350 位元組,而是只為每個 IOQueue 分配 350 位元組(每個隊列一個,以避免爭用)。在擁有 5000 個空閑連接的 WebSocket 服務器中,我們從分配約 1.75 MB(350 位元組*5000)到現在只分配約 2.8kb(350 位元組*8)給 SocketSender 對象。
SslStream 零位元組讀取
無緩衝讀取是我們已經在 ASP.NET Core 中採用的一種技術,以避免在套接字上沒有可用數據時從內存池中租用內存。在這一變化之前,我們的 WebSocket 服務器有 5000 個空閑連接,在沒有 TLS 的情況下需要約 200 MB,而在有 TLS 的情況下需要約 800 MB。其中一些分配(每個連接 4k)是由於 Kestrel 在等待SslStream
上的讀取完成時必須保持ArrayPool
緩衝區。鑒於這些連接是空閑的,沒有一個讀取完成並將其緩衝區返回給ArrayPool
,迫使ArrayPool
分配更多的內存。剩餘的分配都在SslStream
本身。4k 緩衝區用於 TLS 握手,32k 緩衝區用於正常讀取。在預覽版 3 中,當用戶在SslStream
上執行零位元組讀取,而它又沒有可用的數據時,SslStream
會在內部對底層的封裝流執行零位元組讀取。在最好的情況下(空閑連接),這些變化導致每個連接節省了 40 Kb,同時仍然允許消費者(Kestrel)在數據可用時得到通知,而無需保留任何未使用的緩衝區。
PipeReader 零位元組讀取
一旦SslStream
支持無緩衝區讀取,我們就向StreamPipeReader
(將Stream
適配成PipeReader
的內部類型)添加了執行零位元組讀取的選項。在 Kestrel 中,我們使用StreamPipeReader
將底層的SslStream
適配成PipeReader
,有必要在PipeReader
上暴露這些零位元組讀取語義。
現在,你可以使用以下 API 創建一個PipeReader
,支持在任何支持零位元組讀取語義的Stream
上進行零位元組讀取(例如SslStream
、NetworkStream
等)。
var reader = PipeReader.Create(stream, new StreamPipeReaderOptions(useZeroByteReads: true));
從 SlabMemoryPool 中移除板塊
為了減少堆的碎片,Kestrel 採用了一種技術,它分配了 128 KB 的內存板塊作為其內存池的一部分。然後,這些板塊被進一步劃分為 4 KB 的塊,供 Kestrel 內部使用。板塊必須大於 85 KB,以便在大對象堆上強制分配,以盡量防止 GC 重新定位這個陣列。然而,隨着新一代 GC 的引入,Pinned Object Heap(POH),在板塊上分配塊已經沒有意義了。在預覽版 3 中,我們現在直接在 POH 上分配塊[7],降低了管理自己的內存池所涉及的複雜性。這個變化應該可以更容易地執行未來的改進,比如讓 Kestrel 使用的內存池更容易收減。
用於 WPF 和 Windows 窗體的 BlazorWebView 控件
對於 .NET 6,我們增加了對使用 .NET MAUI 和 Blazor 構建跨平台混合桌面應用程序的支持。混合應用程序是利用 Web 技術實現其功能的本地應用程序。例如,一個混合應用程序可能會使用一個嵌入式的 Web 視圖控件來渲染 Web UI。這意味着你可以使用 HTML 和 CSS 等 Web 技術編寫應用程序 UI,同時還可以使用本地設備的功能。我們將在即將發佈的 .NET 6 預覽版中引入對使用 .NET MAUI 和 Blazor 構建混合應用程序的支持。
在這個版本中,我們為 WPF 和 Windows Forms 應用程序引入了BlazorWebView
控件,該控件可將 Blazor 功能嵌入到基於 .NET 6 的現有 Windows 桌面應用程序中。使用 Blazor 和混合方式,你可以將你的 UI 與 WPF 和 Windows Forms 解耦。這是一種對現有桌面應用程序進行現代化改造的好方法,可以將其帶到 .NET MAUI 上或在 Web 上使用。你可以使用 Blazor 對現有的 Windows Forms 和 WPF 應用程序進行現代化改造。
要使用新的BlazorWebView
控件,你首先需要確保你已經安裝了 WebView2[8]。
要將 Blazor 功能添加到現有的 Windows Forms 應用程序中,需要:
-
更新 Windows Forms 應用程序,使其 Target 為 .NET 6。
-
把應用程序項目文件中的 SDK 更新為 Microsoft.NET.Sdk.Razor。
-
添加
Microsoft.AspNetCore.Components.WebView.WindowsForms
包引用。 -
在項目中添加以下
wwwroot/index.html
文件,用實際的項目名稱替換{PROJECT NAME}
:<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>Blazor app</title> <base href="/" /> <link href="{PROJECT NAME}.styles.css" rel="stylesheet" /> <link href="app.css" rel="stylesheet" /> </head> <body> <div id="app"></div> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.webview.js"></script> </body> </html>
-
在 wwwroot 文件夾中添加以下
app.css
文件,包含一些基本樣式:html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } .valid.modified:not([type='checkbox']) { outline: 1px solid #26b050; } .invalid { outline: 1px solid red; } .validation-message { color: red; } #blazor-error-ui { background: lightyellow; bottom: 0; box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); display: none; left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem; position: fixed; width: 100%; z-index: 1000; } #blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; }
-
對於 wwwroot 文件夾中的所有文件,將
Copy to Output Directory
屬性設置為Copy if newer
。 -
在項目中添加一個 Blazor 根組件
Counter.razor
:@using Microsoft.AspNetCore.Components.Web <h1>Counter</h1> <p>The current count is: @currentCount</p> <button @onclick="IncrementCount">Count</button> @code { int currentCount = 0; void IncrementCount() { currentCount++; } }
-
將
BlazorWebView
控件添加到所需的表單中,以渲染 Blazor 根組件:var serviceCollection = new ServiceCollection(); serviceCollection.AddBlazorWebView(); var blazor = new BlazorWebView() { Dock = DockStyle.Fill, HostPage = "wwwroot/index.html", Services = serviceCollection.BuildServiceProvider(), }; blazor.RootComponents.Add<Counter>("#app"); Controls.Add(blazor);
-
運行該應用程序,查看
BlazorWebView
的運行情況。
要將 Blazor 功能添加到現有的 WPF 應用程序中,請按照上面列出的 Windows 窗體應用程序的相同步驟進行操作。另外:
-
為
Microsoft.AspNetCore.Components.WebView.Wpf
替換包引用。 -
在 XAML 中添加
BlazorWebView
控件:<Window x:Class="WpfApp1.MainWindow" xmlns="//schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="//schemas.microsoft.com/winfx/2006/xaml" xmlns:d="//schemas.microsoft.com/expression/blend/2008" xmlns:mc="//schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp1" xmlns:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Wpf;assembly=Microsoft.AspNetCore.Components.WebView.Wpf" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <blazor:BlazorWebView HostPage="wwwroot/index.html" Services="{StaticResource services}"> <blazor:BlazorWebView.RootComponents> <blazor:RootComponent Selector="#app" ComponentType="{x:Type local:Counter}" /> </blazor:BlazorWebView.RootComponents> </blazor:BlazorWebView> </Grid> </Window>
-
將服務提供者設置為靜態資源:
var serviceCollection = new ServiceCollection(); serviceCollection.AddBlazorWebView(); Resources.Add("services", serviceCollection.BuildServiceProvider());
-
為了解決 WPF 運行時構建時找不到 Razor 組件類型的問題,在
Counter.razor.cs
中為組件添加一個空的局部類:public partial class Counter { }
-
構建並運行基於 Blazor 的 WPF 應用:
提供反饋
我們希望你喜歡這個 .NET 6 預覽版中的 ASP.NET Core 部分。我們渴望聽到你對這個版本的體驗。請在 GitHub 上提交 Issue,讓我們知道你的想法。
謝謝你試用 ASP.NET Core!