Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Annotazioni
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Avvertimento
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Questo articolo spiega come ospitare e distribuire app server-side Blazor (Blazor Web Apps e applicazioni Blazor Server) utilizzando ASP.NET Core.
Valori di configurazione dell'host
Le app lato Blazor server possono accettare valori di configurazione host generici.
Distribuzione
Usando un modello di hosting lato server, Blazor viene eseguito nel server dall'interno di un'app ASP.NET Core. Gli aggiornamenti dell'interfaccia utente, la gestione degli eventi e le chiamate JavaScript vengono gestiti tramite una SignalR connessione.
È necessario un server Web in grado di ospitare un'app ASP.NET Core. Visual Studio include un modello di progetto app sul lato server. Per ulteriori informazioni sui modelli di progetto
Pubblicare un'app nella configurazione Release e rilasciare il contenuto della cartella bin/Release/{TARGET FRAMEWORK}/publish, in cui il segnaposto {TARGET FRAMEWORK} è il framework di destinazione.
Scalabilità
Quando si considera la scalabilità verticale di un singolo server, è probabile che la memoria disponibile per un'applicazione sia la prima risorsa che l'app esaurisce man mano che aumentano le richieste degli utenti. La memoria disponibile nel server influisce su:
- Numero di circuiti attivi che un server può supportare.
- Latenza dell'interfaccia utente nel client.
Per indicazioni sulla creazione di app lato Blazor server sicure e scalabili, vedere le risorse seguenti:
- Linee guida per la mitigazione delle minacce per il rendering statico lato server di ASP.NET Core Blazor
- Linee guida per la mitigazione delle minacce per il rendering interattivo lato server di ASP.NET Core Blazor
Ogni circuito usa circa 250 KB di memoria per un'app di tipo Hello World minima. Le dimensioni di un circuito dipendono dal codice dell'app e dai requisiti di manutenzione dello stato associati a ogni componente. Ti consigliamo di misurare le richieste di risorse durante lo sviluppo per l'app e l'infrastruttura, ma la baseline seguente può essere un punto di partenza nella pianificazione della destinazione di distribuzione: se prevedi che l'app supporti 5.000 utenti simultanei, valuta la possibilità di budget di almeno 1,3 GB di memoria server per l'app (o circa 273 KB per utente).
Blazor WebAssembly precaricamento delle risorse statiche
Il componente ResourcePreloader nel contenuto di testa del componente App (App.razor) viene usato per riferire a Blazor risorse statiche. Il componente viene inserito dopo il tag URL di base (<base>):
<ResourcePreloader />
Un Razor componente viene usato invece di <link> elementi perché:
- Il componente consente all'URL di base (valore dell'attributo del tag
<base>) di identificare correttamente la radice dell'apphrefall'interno di un'app ASP.NET Core. - La funzionalità può essere rimossa rimuovendo il
ResourcePreloadertag del componente dalAppcomponente. Ciò è utile nei casi in cui l'app usa unloadBootResourcecallback per modificare gli URL.
Configurazione SignalR
SignalRLe condizioni di hosting e ridimensionamento si applicano alle Blazor app che usano SignalR.
Per ulteriori informazioni su SignalR nelle app Blazor, incluse le linee guida sulla configurazione, vedere le linee guida di ASP.NET Core BlazorSignalR.
Trasporti
Blazor funziona meglio quando si usano WebSocket come SignalR trasporto a causa di una latenza inferiore, maggiore affidabilità e maggiore sicurezza. Long Polling viene usato da SignalR quando WebSockets non sono disponibili o quando l'app è configurata in modo esplicito per utilizzare Long Polling.
Viene visualizzato un avviso della console se viene utilizzato long polling:
Impossibile connettersi tramite WebSocket, utilizzando il trasporto di fallback Long Polling. Ciò può essere dovuto a una VPN o a un proxy che blocca la connessione.
Errori di distribuzione e connessione globali
Raccomandazioni per le distribuzioni globali nei data center geografici:
- Distribuire l'app nelle aree in cui risiede la maggior parte degli utenti.
- Prendere in considerazione l'aumento della latenza per il traffico in tutti i continenti. Per controllare l'aspetto dell'interfaccia utente di riconnessione, consultare la guida di ASP.NET CoreBlazorSignalR.
- Considerare l'uso del Servizio SignalR Azure.
Servizio app di Azure
L'hosting su Azure App Service richiede la configurazione per i WebSocket e l'affinità di sessione, nota anche come affinità di Application Request Routing (ARR).
Annotazioni
Un'app Blazor su Azure App Service non richiede il servizio Azure SignalR.
Abilitare quanto segue per la registrazione dell'app nel servizio app Azure:
- WebSocket per consentire il funzionamento del trasporto WebSocket. L'impostazione predefinita è Disattivata.
- Affinità di sessione per instradare le richieste di un utente alla stessa istanza di App Service. L'impostazione predefinita è Sì.
- Nel portale di Azure, vai all'app Web in Servizi app.
- Aprire Configurazione delle impostazioni>.
- Impostare Web Sockets su Attivo.
- Verificare che l'affinità di sessione sia impostata su Sì.
Servizio di Azure SignalR
Il servizio di Azure SignalR facoltativo funziona insieme all'hub dell'app SignalR per aumentare le prestazioni di un'app lato server a un numero elevato di connessioni simultanee. Inoltre, la copertura globale del servizio e i data center ad alte prestazioni aiutano significativamente a ridurre la latenza a causa della geografia.
Il servizio non è necessario per Blazor le app ospitate nel servizio app Azure o nelle app di Azure Container, ma può essere utile in altri ambienti di hosting:
- Per agevolare l'espansione delle connessioni su larga scala.
- Gestire la distribuzione globale.
Il servizio Azure SignalR con SDK v1.26.1 o versione successiva supporta SignalR riconnessione con stato (WithStatefulReconnect).
Se l'app utilizza Long Polling o esegue il fallback su Long Polling invece di ai WebSocket, potrebbe essere necessario configurare l'intervallo di polling massimo (MaxPollIntervalInSeconds, impostazione predefinita: 5 secondi, limite: 1-300 secondi), che definisce l'intervallo di polling massimo consentito per le connessioni di Long Polling nel servizio AzureSignalR. Se la richiesta di polling successiva non arriva entro l'intervallo massimo di polling, il servizio chiude la connessione client.
Per indicazioni su come aggiungere il servizio come dipendenza a una distribuzione di produzione, vedere Pubblicare un'app ASP.NET Core nel servizio app di Azure.
Per altre informazioni, vedere:
- Servizio di Azure SignalR
- Che cos'è il servizio di Azure SignalR ?
- Hosting e scalabilità in produzione di ASP.NET Core SignalR
- Pubblicare un'app core SignalR ASP.NET nel servizio app Azure
App contenitore di Azure
Per un'esplorazione più approfondita del ridimensionamento delle app lato Blazor server nel servizio App Contenitore di Azure, vedere Ridimensionamento delle app di base ASP.NET in Azure. L'esercitazione illustra come creare e integrare i servizi necessari per ospitare app su Azure Container Apps. In questa sezione vengono forniti anche i passaggi di base.
Configurare il servizio Azure Container Apps per l'affinità di sessione seguendo le indicazioni riportate in Affinità di sessione in Azure Container Apps (documentazione di Azure).
Il servizio Protezione dati di base (DP) ASP.NET deve essere configurato per rendere persistenti le chiavi in una posizione centralizzata a cui tutte le istanze del contenitore possono accedere. Le chiavi possono essere archiviate in Archiviazione BLOB di Azure e protette con Azure Key Vault. Il servizio Device Provisioning usa le chiavi per deserializzare i componenti Razor. Per configurare il servizio DP per l'uso di Azure Blob Storage e Azure Key Vault, fare riferimento ai pacchetti NuGet seguenti:
-
Azure.Identity: fornisce classi da usare con i servizi di gestione delle identità e degli accessi di Azure. -
Microsoft.Extensions.Azure: fornisce metodi di estensione utili per eseguire le configurazioni di Base di Azure. -
Azure.Extensions.AspNetCore.DataProtection.Blobs: Consente di archiviare chiavi di protezione dati di ASP.NET Core in Archiviazione Blob di Azure in modo che le chiavi possano essere condivise tra più istanze di un'applicazione web. -
Azure.Extensions.AspNetCore.DataProtection.Keys: consente di proteggere le chiavi a riposo usando la funzionalità di crittografia e wrapping di chiavi di Azure Key Vault.
Annotazioni
Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Verificare le versioni corrette dei pacchetti in NuGet.org.
-
Eseguire l'aggiornamento
Program.cscon il codice evidenziato seguente: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();Le modifiche precedenti consentono all'app di gestire il servizio Device Provisioning usando un'architettura centralizzata e scalabile. DefaultAzureCredential individua l'identità gestita dell'app contenitore dopo che il codice è stato distribuito in Azure e la utilizza per connettersi all'archiviazione BLOB e al key vault dell'app.
Per creare l'identità gestita per l'app contenitore e concedergli l'accesso all'archiviazione BLOB e a un key vault, seguire questa procedura:
- Nel portale di Azure passare alla pagina di panoramica dell'app contenitore.
- Selezionare Service Connector dal riquadro di spostamento sinistro.
- Selezionare + Crea nella barra di spostamento superiore.
-
Nel menu a comparsa Crea connessione immettere i valori seguenti:
- Contenitore: selezionare l'app contenitore creata per ospitare l'app.
- Tipo di servizio: selezionare Archiviazione BLOB.
- Sottoscrizione: selezionare la sottoscrizione proprietaria dell'app contenitore.
-
Nome connessione: immettere un nome di
scalablerazorstorage. - Tipo di client: selezionare .NET e quindi selezionare Avanti.
- Selezionare Identità gestita assegnata dal sistema e selezionare Avanti.
- Usare le impostazioni di rete predefinite e selezionare Avanti.
- Dopo che Azure convalida le impostazioni, selezionare Crea.
Ripetere le impostazioni precedenti per la cassaforte delle chiavi. Selezionare il servizio di Key Vault appropriato e inserire la chiave nella scheda Base.
Annotazioni
L'esempio precedente usa DefaultAzureCredential per semplificare l'autenticazione durante lo sviluppo di app distribuite in Azure combinando le credenziali usate negli ambienti di hosting di Azure con le credenziali usate nello sviluppo locale. Quando si passa all'ambiente di produzione, un'alternativa è una scelta migliore, ad esempio ManagedIdentityCredential. Per altre informazioni, vedere Autenticare le app .NET ospitate in Azure nelle risorse di Azure usando un'identità gestita assegnata dal sistema.
IIS
Quando si usa IIS, abilitare:
Per altre informazioni, vedere le linee guida e i collegamenti incrociati delle risorse IIS esterne in Pubblicare un'app ASP.NET Core in IIS.
Kubernetes
Creare una definizione di ingresso usando le seguenti annotazioni di Kubernetes per l'affinità di sessione:
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 con Nginx
Seguire le indicazioni per un'app SignalR ASP.NET Core con le modifiche seguenti:
- Modificare il
locationpercorso da/hubroute(location /hubroute { ... }) al percorso radice/(location / { ... }). - Rimuovere la configurazione per il buffering proxy (
proxy_buffering off;) perché l'impostazione si applica solo agli eventi inviati dal server (SSE), che non sono rilevanti per Blazor le interazioni client-server dell'app.
Per altre informazioni e indicazioni sulla configurazione, vedere le risorse seguenti:
- Hosting e scalabilità in produzione di ASP.NET Core SignalR
- Hostare ASP.NET Core su Linux con Nginx
- Configurare ASP.NET Core per l'utilizzo di server proxy e servizi di bilanciamento del carico
- NGINX come proxy WebSocket
- Proxying di WebSocket
- Consultare gli sviluppatori nei forum di supporto non Microsoft:
Linux con Apache
Per ospitare un'app Blazor dietro Apache in Linux, configurare ProxyPass per il traffico HTTP e WebSocket.
Nell'esempio seguente:
- Kestrel il server è in esecuzione sulla macchina host.
- L'app rimane in ascolto del traffico sulla porta 5000.
ProxyPreserveHost On
ProxyPassMatch ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass /_blazor ws://localhost:5000/_blazor
ProxyPass / http://localhost:5000/
ProxyPassReverse / http://localhost:5000/
Abilitare i moduli seguenti:
a2enmod proxy
a2enmod proxy_wstunnel
Controllare la presenza di errori webSocket nella console del browser. Errori di esempio:
- Firefox non riesce a stabilire una connessione al server all'indirizzo ws://the-domain-name.tld/_blazor?id=XXX
- Errore: Impossibile avviare il trasporto 'WebSockets': Errore: Si è verificato un problema con il trasporto.
- Errore: Impossibile avviare il trasporto 'LongPolling': TypeError: this.transport non è definito
- Errore: impossibile connettersi al server con uno dei trasporti disponibili. WebSocket non riuscito
- Errore: impossibile inviare dati se la connessione non è nello stato "Connesso".
Per altre informazioni e indicazioni sulla configurazione, vedere le risorse seguenti:
- Configurare ASP.NET Core per l'utilizzo di server proxy e servizi di bilanciamento del carico
- Documentazione di Apache
- Consultare gli sviluppatori nei forum di supporto non Microsoft:
Misurare la latenza di rete
JS l'interoperabilità può essere usata per misurare la latenza di rete, come illustrato nell'esempio seguente.
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();
}
}
}
Per un'esperienza di interfaccia utente ragionevole, è consigliabile una latenza dell'interfaccia utente sostenuta di 250 ms o meno.