Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.
Warnung
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.
In diesem Artikel wird erläutert, wie Sie serverseitige Blazor Apps (Blazor Web Apps und Blazor Server Apps) mit ASP.NET Core hosten und bereitstellen.
Hostkonfigurationswerte
Serverseitige Blazor Apps können generische Hostkonfigurationswerte akzeptieren.
Einsatz
Mithilfe eines serverseitigen Hostingmodells Blazor wird auf dem Server innerhalb einer ASP.NET Core-App ausgeführt. Ui-Updates, Ereignisbehandlung und JavaScript-Aufrufe werden über eine SignalR Verbindung verarbeitet.
Ein Webserver, der eine ASP.NET Core-App hosten kann, ist erforderlich. Visual Studio enthält eine serverseitige App-Projektvorlage. Weitere Informationen zu Blazor-Projektvorlagen finden Sie unter Blazor-Projektstruktur in ASP.NET Core.
Veröffentlichen Sie eine App in der Releasekonfiguration, und stellen Sie den Inhalt des bin/Release/{TARGET FRAMEWORK}/publish Ordners bereit, wobei der {TARGET FRAMEWORK} Platzhalter das Zielframework ist.
Skalierbarkeit
Bei der Skalierbarkeit eines einzelnen Servers (Skalierung nach oben) ist der für eine App verfügbare Speicher wahrscheinlich die erste Ressource, die die App ausschöpfen kann, wenn die Benutzeranforderungen erhöhen. Der verfügbare Arbeitsspeicher auf dem Server wirkt sich auf folgendes aus:
- Anzahl der aktiven Schaltkreise, die ein Server unterstützen kann.
- Benutzeroberflächenlatenz auf dem Client.
Anleitungen zum Erstellen sicherer und skalierbarer serverseitiger Blazor Apps finden Sie in den folgenden Ressourcen:
- Leitfaden zur Bedrohungsminderung für ASP.NET Core Blazor statisches serverseitiges Rendering
- Leitfaden zur Bedrohungsminderung für ASP.NET Core Blazor interaktives serverseitiges Rendering
Jeder Schaltkreis verwendet ca. 250 KB Arbeitsspeicher für eine minimale Hello World-App. Die Größe eines Schaltkreises hängt vom Code der App und den Zustandswartungsanforderungen ab, die den einzelnen Komponenten zugeordnet sind. Es wird empfohlen, die Ressourcenanforderungen während der Entwicklung für Ihre App und Infrastruktur zu messen, aber der folgende Basisplan kann ein Ausgangspunkt bei der Planung Ihres Bereitstellungsziels sein: Wenn Sie erwarten, dass Ihre App 5.000 gleichzeitige Benutzer unterstützt, sollten Sie die Budgetierung mindestens 1,3 GB Serverspeicher für die App (oder ~273 KB pro Benutzer) in Betracht ziehen.
Blazor WebAssembly Vorladen statischer Ressourcen
Die ResourcePreloader-Komponente im Kopfinhalt der App-Komponente (App.razor) wird verwendet, um auf Blazor statische Assets zu verweisen. Die Komponente wird nach dem Basis-URL-Tag (<base>) platziert.
<ResourcePreloader />
Anstelle von Razor Elementen wird eine <link> Komponente verwendet, da:
- Die Komponente erlaubt es, mithilfe des Basis-URL-Attributwerts (
<base>Taghref) den Stamm der Blazor-App innerhalb einer ASP.NET Core-App korrekt zu identifizieren. - Das Feature kann entfernt werden, indem das
ResourcePreloaderKomponenten-Tag aus derAppKomponente entfernt wird. Dies ist in Fällen hilfreich, in denen die App einenloadBootResourceRückruf zum Ändern von URLs verwendet.
SignalR Konfiguration
SignalRHosting- und Skalierungsbedingungen gelten für Blazor Apps, die SignalR verwenden.
Weitere Informationen zu SignalR in Blazor Apps, einschließlich Konfigurationsanleitungen, finden Sie unter ASP.NET CoreBlazorSignalR-Leitfaden.
Transporte
Blazor funktioniert am besten, wenn WebSockets als SignalR Transport aufgrund geringerer Latenz, besserer Zuverlässigkeit und verbesserter Sicherheit verwendet werden. Long Polling wird von SignalR verwendet, wenn WebSockets nicht verfügbar ist oder wenn die App explizit so konfiguriert ist, dass Long Polling verwendet wird.
Eine Konsolenwarnung wird angezeigt, wenn Long Polling verwendet wird:
Fehler beim Herstellen einer Verbindung über WebSockets mithilfe des Long Polling-Fallbacktransports. Dies kann auf ein VPN oder einen Proxy zurückzuführen sein, der die Verbindung blockiert.
Globale Bereitstellungs- und Verbindungsfehler
Empfehlungen für globale Bereitstellungen in geografischen Rechenzentren:
- Stellen Sie die App in den Regionen bereit, in denen sich die meisten Benutzer befinden.
- Berücksichtigen Sie die erhöhte Latenz für Datenverkehr über Kontinente hinweg. Informationen zum Steuern der Darstellung der Benutzeroberfläche für die erneute Verbindung finden Sie unter ASP.NET Core-AnleitungBlazorSignalR.
- Erwägen Sie die Verwendung des Azure-DienstsSignalR.
Azure App Service
Das Hosting auf Azure App Service erfordert die Konfiguration von WebSockets und Sitzungsaffinität, auch als Application Request Routing (ARR)-Affinität bezeichnet.
Hinweis
Eine Blazor App in Azure App Service erfordert keinen Azure SignalR Service.
Aktivieren Sie Folgendes für die App-Registrierung in Azure App Service:
- WebSockets um den WebSockets-Datentransport zuzulassen. Die Standardeinstellung ist Aus.
- Sitzungsaffinität, um Anfragen eines Benutzers zurück an dieselbe App Service-Instanz zu leiten. Die Standardeinstellung ist Ein.
- Navigieren Sie im Azure-Portal in App Services zur Web-App.
- Öffnen Sie Einstellungen>Konfiguration.
- Legen Sie WebSockets auf Ein fest.
- Überprüfen Sie, ob die Sitzungsaffinität auf Ein eingestellt ist.
Azure SignalR Dienst
Der optionale Azure SignalR-Dienst arbeitet mit dem SignalR-Hub der App zusammen, um eine serverseitige App für eine große Anzahl gleichzeitiger Verbindungen hochskalieren zu können. Außerdem tragen die globale Reichweite und die Hochleistungsrechenzentren von Service erheblich zur Verringerung der geografiebedingten Latenz bei.
Der Dienst ist nicht für Blazor-Apps erforderlich, die in Azure App Service oder Azure Container Apps gehostet werden, aber in anderen Hostingumgebungen hilfreich sein können:
- So erleichtern Sie das horizontale Skalieren der Verbindung:
- Verwaltung der globalen Verteilung.
Der Azure SignalR-Dienst mit SDK v1.26.1 oder höher unterstützt SignalR statusbasiertes Wiederverbinden (WithStatefulReconnect).
Für den Fall, dass die App long Polling verwendet oder auf "Long Polling" anstelle von WebSockets zurückgreift, müssen Sie möglicherweise das maximale Abrufintervall konfigurieren (MaxPollIntervalInSecondsStandard: 5 Sekunden, Grenzwert: 1-300 Sekunden), das das maximale Abrufintervall definiert, das für lange Abrufverbindungen im Azure-Dienst SignalR zulässig ist. Wenn die nächste Abfrageanforderung nicht innerhalb des maximalen Abrufintervalls eingeht, schließt der Dienst die Clientverbindung.
Anleitungen zum Hinzufügen des Diensts als Abhängigkeit zu einer Produktionsbereitstellung finden Sie unter Veröffentlichen einer ASP.NET Core-App SignalR in Azure App Service.
Weitere Informationen finden Sie unter:
- Azure SignalR Service
- Was ist Azure SignalR Service?
- ASP.NET Core: Hosting in der Produktion und SkalierungSignalR
- Veröffentlichen einer ASP.NET Core-SignalR-App bei Azure App Service
Azure Container Apps – ein Dienst für containerbasierte Anwendungen
Eine tiefere Erkundung der Skalierung serverseitiger Blazor Apps im Azure-Container-Apps-Dienst finden Sie unter Skalierung ASP.NET Core Apps in Azure. Im Lernprogramm wird erläutert, wie Sie die dienste erstellen und integrieren, die zum Hosten von Apps in Azure-Container-Apps erforderlich sind. Grundlegende Schritte werden auch in diesem Abschnitt beschrieben.
Konfigurieren Sie den Azure-Container-Apps-Dienst für die Sitzungsaffinität, indem Sie die Anleitung in der Sitzungsaffinität in Azure-Container-Apps (Azure-Dokumentation) ausführen.
Der ASP.NET Core Data Protection (DP)-Dienst muss so konfiguriert werden, dass Schlüssel an einem zentralen Speicherort beibehalten werden, auf den alle Containerinstanzen zugreifen können. Die Schlüssel können in Azure Blob Storage gespeichert und mit Azure Key Vault geschützt werden. Der DP-Dienst verwendet die Schlüssel, um Razor Komponenten zu deserialisieren. Um den DP-Dienst für die Verwendung von Azure Blob Storage und Azure Key Vault zu konfigurieren, verweisen Sie auf die folgenden NuGet-Pakete:
-
Azure.Identity: Stellt Klassen für die Arbeit mit den Azure-Identitäts- und Zugriffsverwaltungsdiensten bereit. -
Microsoft.Extensions.Azure: Stellt hilfreiche Erweiterungsmethoden zum Ausführen von Azure-Kernkonfigurationen bereit. -
Azure.Extensions.AspNetCore.DataProtection.Blobs: Ermöglicht das Speichern von ASP.NET Kerndatenschutzschlüsseln in Azure Blob Storage, sodass Schlüssel über mehrere Instanzen einer Web-App hinweg freigegeben werden können. -
Azure.Extensions.AspNetCore.DataProtection.Keys: Ermöglicht den Schutz ruhender Schlüssel mithilfe der Azure Key Vault Key Encryption/Wrapping-Funktion.
Hinweis
Eine Anleitung zum Hinzufügen von Paketen zu .NET-Anwendungen finden Sie in den Artikeln unter Pakete installieren und verwalten unter Workflow für die Paketnutzung (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.
-
Aktualisieren Sie
Program.csmit dem folgenden hervorgehobenen Code: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();Mit den vorherigen Änderungen kann die App den DP-Dienst mithilfe einer zentralen, skalierbaren Architektur verwalten. DefaultAzureCredential ermittelt die verwaltete Identität der Container-App, nachdem der Code in Azure bereitgestellt wurde, und verwendet sie, um eine Verbindung mit Blob Storage und dem Schlüsseltresor der App herzustellen.
Führen Sie die folgenden Schritte aus, um die verwaltete Identität der Container-App zu erstellen und ihr Zugriff auf Blobspeicher und einen Schlüsseltresor zu gewähren:
- Navigieren Sie im Azure-Portal zur Übersichtsseite der Container-App.
- Wählen Sie "Service Connector" aus der linken Navigation aus.
- Wählen Sie +Erstellen aus der oberen Navigationsleiste aus.
- Geben Sie im Flyoutmenü "Verbindung erstellen " die folgenden Werte ein:
- Container: Wählen Sie die Container-App aus, die Sie zum Hosten Ihrer App erstellt haben.
- Diensttyp: Wählen Sie Blob Storage aus.
- Abonnement: Wählen Sie das Abonnement aus, das der Container-App gehört.
-
Verbindungsname: Geben Sie einen Namen von
scalablerazorstorage. - Clienttyp: Wählen Sie .NET und dann "Weiter" aus.
- Wählen Sie "Vom System zugewiesene verwaltete Identität " und dann "Weiter" aus.
- Verwenden Sie die Standardnetzwerkeinstellungen, und wählen Sie "Weiter" aus.
- Wählen Sie Erstellen aus, nachdem Azure die Einstellungen überprüft hat.
Wiederholen Sie die vorherigen Einstellungen für den Schlüsseltresor. Wählen Sie den entsprechenden Schlüsseltresordienst und Schlüssel auf der Registerkarte Grundlagen aus.
Hinweis
Im vorherigen Beispiel wird DefaultAzureCredential verwendet, um die Authentifizierung zu vereinfachen, indem Anmeldeinformationen in Azure-Hostingumgebungen mit Anmeldeinformationen kombiniert werden, die in der lokalen Entwicklung genutzt werden, während Apps entwickelt werden, die für Azure bereitgestellt werden. Beim Wechsel in die Produktion ist eine Alternative die bessere Wahl, wie zum Beispiel ManagedIdentityCredential. Weitere Informationen finden Sie unter Authentifizieren von von Azure gehosteten .NET-Apps für Azure-Ressourcen mithilfe einer vom System zugewiesenen verwalteten Identität.
IIS
Aktivieren Sie bei Verwendung von IIS Folgendes:
Weitere Informationen finden Sie in den Anleitungen und externen IIS-Ressourcen-Querverknüpfungen in der Veröffentlichung einer ASP.NET Core-App in IIS.
Kubernetes
Erstellen Sie eine Eingangsdefinition mit den folgenden Kubernetes-Anmerkungen für die Sitzungsaffinität:
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 mit Nginx
Folgen Sie der Anleitung für eine SignalR-App in ASP.NET Core mit den folgenden Änderungen:
- Ändern Sie den
locationPfad von/hubroute(location /hubroute { ... }) in den Stammpfad/(location / { ... }). - Entfernen Sie die Konfiguration für die Proxypufferung (
proxy_buffering off;), da die Einstellung nur für vom Server gesendete Ereignisse (Server-Sent Events, SSE) gilt, die für die Interaktion zwischen Blazor-App-Client und -Server nicht relevant sind.
Weitere Informationen und einen Leitfaden zur Konfiguration finden Sie in den folgenden Ressourcen:
- ASP.NET Core: Hosting in der Produktion und SkalierungSignalR
- Hosten von ASP.NET Core unter Linux mit nginx
- Konfigurieren von ASP.NET Core zur Verwendung mit Proxyservern und Lastenausgleich
- NGINX als WebSocket-Proxy
- WebSocket-Proxyfunktion
- Wenden Sie sich an Entwickler in Nicht-Microsoft-Supportforen:
Linux mit Apache
Um eine Blazor App hinter Apache unter Linux zu hosten, konfigurieren Sie ProxyPass für HTTP- und WebSocket-Datenverkehr.
Im folgenden Beispiel:
- Kestrel Der Server wird auf dem Hostcomputer ausgeführt.
- Die App lauscht an Port 5000 auf Datenverkehr.
ProxyPreserveHost On
ProxyPassMatch ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass /_blazor ws://localhost:5000/_blazor
ProxyPass / http://localhost:5000/
ProxyPassReverse / http://localhost:5000/
Aktivieren Sie die folgenden Module:
a2enmod proxy
a2enmod proxy_wstunnel
Überprüfen Sie die Browserkonsole auf WebSockets-Fehler. Beispielfehler:
- Firefox kann keine Verbindung mit dem Server herstellen unter ws://the-domain-name.tld/_blazor?id=XXX
- Fehler: Der Transport 'WebSockets' konnte nicht gestartet werden: Fehler: Es gab ein Problem mit dem Transport.
- Fehler: "Failed to start the transport 'LongPolling'" (Beim Starten des Transports „LongPolling“ ist ein Fehler aufgetreten): "TypeError: this.transport is undefined" (TypeError: Dieser Transport ist nicht definiert)
- Fehler: Mit einem der verfügbaren Transporte kann keine Verbindung mit dem Server hergestellt werden. Fehler bei WebSockets
- Fehler: Daten können nicht gesendet werden, wenn sich die Verbindung nicht im Zustand "Verbunden" befindet.
Weitere Informationen und einen Leitfaden zur Konfiguration finden Sie in den folgenden Ressourcen:
- Konfigurieren von ASP.NET Core zur Verwendung mit Proxyservern und Lastenausgleich
- Apache-Dokumentation
- Wenden Sie sich an Entwickler in Nicht-Microsoft-Supportforen:
Messen der Netzwerklatenz
JS Interop kann verwendet werden, um die Netzwerklatenz zu messen, wie im folgenden Beispiel veranschaulicht.
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();
}
}
}
Für eine angemessene Benutzeroberfläche empfehlen wir eine dauerhafte UI-Latenz von 250 ms oder weniger.