Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Примечание.
Это не последняя версия этой статьи. Для текущего релиза смотрите версию .NET 9 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. Для текущего релиза смотрите версию .NET 9 этой статьи.
Это важно
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Корпорация Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых в отношении информации, предоставленной здесь.
Для текущего релиза смотрите версию .NET 9 этой статьи.
This article explains how to host and deploy server-side Blazor apps (Blazor Web Apps and Blazor Server apps) using ASP.NET Core.
Значения конфигурации хоста
Server-side Blazor apps can accept Generic Host configuration values.
Развертывание
Using a server-side hosting model, Blazor is executed on the server from within an ASP.NET Core app. UI updates, event handling, and JavaScript calls are handled over a SignalR connection.
A web server capable of hosting an ASP.NET Core app is required. Visual Studio includes a server-side app project template. For more information on Blazor project templates, see ASP.NET Core Blazor project structure.
Publish an app in Release configuration and deploy the contents of the bin/Release/{TARGET FRAMEWORK}/publish
folder, where the {TARGET FRAMEWORK}
placeholder is the target framework.
Масштабируемость
When considering the scalability of a single server (scale up), the memory available to an app is likely the first resource that the app exhausts as user demands increase. The available memory on the server affects the:
- Number of active circuits that a server can support.
- UI latency on the client.
For guidance on building secure and scalable server-side Blazor apps, see the following resources:
- Threat mitigation guidance for ASP.NET Core Blazor static server-side rendering
- Руководство по снижению угроз для интерактивного серверного рендеринга в ASP.NET Core Blazor
Each circuit uses approximately 250 KB of memory for a minimal Hello World-style app. The size of a circuit depends on the app's code and the state maintenance requirements associated with each component. We recommend that you measure resource demands during development for your app and infrastructure, but the following baseline can be a starting point in planning your deployment target: If you expect your app to support 5,000 concurrent users, consider budgeting at least 1.3 GB of server memory to the app (or ~273 KB per user).
SignalRКонфигурация
SignalR's hosting and scaling conditions apply to Blazor apps that use SignalR.
For more information on SignalR in Blazor apps, including configuration guidance, see ASP.NET Core BlazorSignalR guidance.
Transports
Blazor works best when using WebSockets as the SignalR transport due to lower latency, better reliability, and improved security. Long Polling is used by SignalR when WebSockets isn't available or when the app is explicitly configured to use Long Polling.
A console warning appears if Long Polling is utilized:
Failed to connect via WebSockets, using the Long Polling fallback transport. This may be due to a VPN or proxy blocking the connection.
Global deployment and connection failures
Recommendations for global deployments to geographical data centers:
- Deploy the app to the regions where most of the users reside.
- Take into consideration the increased latency for traffic across continents. To control the appearance of the reconnection UI, see ASP.NET Core BlazorSignalR guidance.
- Consider using the Azure SignalR Service.
Служба приложений Azure
Hosting on Azure App Service requires configuration for WebSockets and session affinity, also called Application Request Routing (ARR) affinity.
Примечание.
A Blazor app on Azure App Service doesn't require Azure SignalR Service.
Enable the following for the app's registration in Azure App Service:
- WebSockets to allow the WebSockets transport to function. The default setting is Off.
- Session affinity to route requests from a user back to the same App Service instance. The default setting is On.
- In the Azure portal, navigate to the web app in App Services.
- Open Settings>Configuration.
- Set Web sockets to On.
- Verify that Session affinity is set to On.
Служба Azure SignalR
Необязательная служба
Служба не требуется для Blazor приложений, размещенных в службе приложение Azure или приложениях контейнеров Azure, но может быть полезно в других средах размещения:
- To facilitate connection scale out.
- Управление глобальным распределением.
The Azure SignalR Service with SDK v1.26.1 or later supports SignalR stateful reconnect (WithStatefulReconnect).
In the event that the app uses Long Polling or falls back to Long Polling instead of WebSockets, you may need to configure the maximum poll interval (MaxPollIntervalInSeconds
, default: 5 seconds, limit: 1-300 seconds), which defines the maximum poll interval allowed for Long Polling connections in the Azure SignalR Service. If the next poll request doesn't arrive within the maximum poll interval, the service closes the client connection.
For guidance on how to add the service as a dependency to a production deployment, see Publish an ASP.NET Core SignalR app to Azure App Service.
Дополнительные сведения можно найти здесь
- Azure SignalR Service
- Что такое служба SignalR Azure?
- ASP.NET Core SignalR production hosting and scaling
- Публикация приложения ASP.NET Core SignalR в Службе приложений Azure.
Приложения контейнеров Azure
For a deeper exploration of scaling server-side Blazor apps on the Azure Container Apps service, see Scaling ASP.NET Core Apps on Azure. The tutorial explains how to create and integrate the services required to host apps on Azure Container Apps. Basic steps are also provided in this section.
Configure Azure Container Apps service for session affinity by following the guidance in Session Affinity in Azure Container Apps (Azure documentation).
The ASP.NET Core Data Protection (DP) service must be configured to persist keys in a centralized location that all container instances can access. The keys can be stored in Azure Blob Storage and protected with Azure Key Vault. The DP service uses the keys to deserialize Razor components. To configure the DP service to use Azure Blob Storage and Azure Key Vault, reference the following NuGet packages:
-
Azure.Identity
: Provides classes to work with the Azure identity and access management services. -
Microsoft.Extensions.Azure
: Provides helpful extension methods to perform core Azure configurations. -
Azure.Extensions.AspNetCore.DataProtection.Blobs
: Allows storing ASP.NET Core Data Protection keys in Azure Blob Storage so that keys can be shared across several instances of a web app. -
Azure.Extensions.AspNetCore.DataProtection.Keys
: Enables protecting keys at rest using the Azure Key Vault Key Encryption/Wrapping feature.
Примечание.
Рекомендации по добавлению пакетов в приложения .NET см. в статьях, приведенных в разделе Установка пакетов и управление ими в рабочий процесс использования пакетов (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.
-
Обновите
Program.cs
, используя выделенный код:using Azure.Identity; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.Azure; var builder = WebApplication.CreateBuilder(args); var BlobStorageUri = builder.Configuration["AzureURIs:BlobStorage"]; var KeyVaultURI = builder.Configuration["AzureURIs:KeyVault"]; builder.Services.AddRazorPages(); builder.Services.AddHttpClient(); builder.Services.AddServerSideBlazor(); builder.Services.AddAzureClientsCore(); builder.Services.AddDataProtection() .PersistKeysToAzureBlobStorage(new Uri(BlobStorageUri), new DefaultAzureCredential()) .ProtectKeysWithAzureKeyVault(new Uri(KeyVaultURI), new DefaultAzureCredential()); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
The preceding changes allow the app to manage the DP service using a centralized, scalable architecture. DefaultAzureCredential discovers the container app managed identity after the code is deployed to Azure and uses it to connect to blob storage and the app's key vault.
To create the container app managed identity and grant it access to blob storage and a key vault, complete the following steps:
- In the Azure Portal, navigate to the overview page of the container app.
- Select Service Connector from the left navigation.
- Select + Create from the top navigation.
- In the Create connection flyout menu, enter the following values:
- Container: Select the container app you created to host your app.
- Service type: Select Blob Storage.
- Subscription: Select the subscription that owns the container app.
-
Connection name: Enter a name of
scalablerazorstorage
. - Client type: Select .NET and then select Next.
- Select System assigned managed identity and select Next.
- Use the default network settings and select Next.
- After Azure validates the settings, select Create.
Repeat the preceding settings for the key vault. Select the appropriate key vault service and key in the Basics tab.
IIS
When using IIS, enable:
For more information, see the guidance and external IIS resource cross-links in Publish an ASP.NET Core app to IIS.
Kubernetes
Create an ingress definition with the following Kubernetes annotations for session affinity:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: <ingress-name>
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "affinity"
nginx.ingress.kubernetes.io/session-cookie-expires: "14400"
nginx.ingress.kubernetes.io/session-cookie-max-age: "14400"
Linux с Nginx
Следуйте указаниям для приложения ASP.NET Core SignalR со следующими изменениями:
- Change the
location
path from/hubroute
(location /hubroute { ... }
) to the root path/
(location / { ... }
). - Удалите конфигурацию для буферизации прокси-сервера (
proxy_buffering off;
), так как этот параметр применяется только к событиям, отправленным сервером (SSE), которые не относятся к взаимодействию между клиентом и сервером в приложении Blazor.
Дополнительные сведения и инструкции по настройке см. в следующих ресурсах:
- ASP.NET Core SignalR production hosting and scaling
- Разместите ASP.NET Core на Linux с Nginx
- Настройка ASP.NET Core для работы с прокси-серверами и подсистемами балансировки нагрузки
- NGINX как прокси-сервер WebSocket
- Использование прокси-сервера для WebSocket
- Consult developers on non-Microsoft support forums:
Linux with Apache
To host a Blazor app behind Apache on Linux, configure ProxyPass
for HTTP and WebSockets traffic.
В следующем примере :
- Kestrel server is running on the host machine.
- The app listens for traffic on port 5000.
ProxyPreserveHost On
ProxyPassMatch ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass /_blazor ws://localhost:5000/_blazor
ProxyPass / http://localhost:5000/
ProxyPassReverse / http://localhost:5000/
Enable the following modules:
a2enmod proxy
a2enmod proxy_wstunnel
Check the browser console for WebSockets errors. Example errors:
- Firefox can't establish a connection to the server at ws://the-domain-name.tld/_blazor?id=XXX
- Error: Failed to start the transport 'WebSockets': Error: There was an error with the transport.
- Error: Failed to start the transport 'LongPolling': TypeError: this.transport is undefined
- Error: Unable to connect to the server with any of the available transports. WebSockets failed
- Error: Cannot send data if the connection is not in the 'Connected' State.
Дополнительные сведения и инструкции по настройке см. в следующих ресурсах:
- Настройка ASP.NET Core для работы с прокси-серверами и подсистемами балансировки нагрузки
- Apache documentation
- Consult developers on non-Microsoft support forums:
Measure network latency
JS interop can be used to measure network latency, as the following example demonstrates.
MeasureLatency.razor
:
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
<h2>Measure Latency</h2>
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
@inject IJSRuntime JS
@if (latency is null)
{
<span>Calculating...</span>
}
else
{
<span>@(latency.Value.TotalMilliseconds)ms</span>
}
@code {
private DateTime startTime;
private TimeSpan? latency;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
startTime = DateTime.UtcNow;
var _ = await JS.InvokeAsync<string>("toString");
latency = DateTime.UtcNow - startTime;
StateHasChanged();
}
}
}
For a reasonable UI experience, we recommend a sustained UI latency of 250 ms or less.
ASP.NET Core