Hosten und Bereitstellen von serverseitigen Blazor-Apps

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird erläutert, wie Sie serverseitige Blazor-Apps mit ASP.NET Core hosten und bereitstellen.

Hostkonfigurationswerte

Serverseitige Blazor-Apps können generische Hostkonfigurationswerte akzeptieren.

Bereitstellung

Mithilfe eines serverseitigen Hostingmodells wird Blazor innerhalb einer ASP.NET Core-App auf dem Server ausgeführt. Benutzeroberflächenupdates, Ereignisbehandlung und JavaScript-Aufrufe werden über eine SignalR-Verbindung verarbeitet.

Hierfür wird ein Webserver benötigt, der eine ASP.NET Core-App hosten kann. Visual Studio enthält eine Projektvorlage für serverseitige Apps. Weitere Informationen zu Blazor-Projektvorlagen finden Sie unter Blazor-Projektstruktur in ASP.NET Core.

Skalierbarkeit

Bei Betrachtung der Skalierbarkeit eines einzelnen Servers (zum Hochskalieren) ist der einer App zur Verfügung stehende Arbeitsspeicher wahrscheinlich die erste Ressource, die von der App bei steigenden Benutzeranforderungen ausgeschöpft wird. Der verfügbare Arbeitsspeicher auf dem Server wirkt sich auf Folgendes aus:

  • Anzahl der aktiven Verbindungen, 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:

Jede Verbindung verwendet ungefähr 250 KB Arbeitsspeicher für eine minimale App im Hello World-Stil. Die Größe einer Verbindung hängt vom App-Code und den Zustandsverwaltungsanforderungen der einzelnen Komponenten ab. Sie sollten die Ressourcenanforderungen während der Entwicklung für Ihre App und die Infrastruktur messen, aber die folgende Baseline kann ein Ausgangspunkt zur Planung des Bereitstellungsziels sein: Wenn Sie davon ausgehen, dass Ihre App 5.000 gleichzeitige Benutzer unterstützt, sollten Sie erwägen, mindestens 1,3 GB Serverarbeitsspeicher (oder ~273 KB pro Benutzer) für die App einzukalkulieren.

SignalR-Konfiguration

Die Hosting- und Skalierungsbedingungen von SignalR gelten für Blazor-Apps, die SignalR verwenden.

Weitere Informationen zu SignalR in Blazor-Apps, einschließlich Konfigurationsleitfäden, finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.

Transportprotokolle

Blazor funktioniert am besten, wenn für den SignalR-Transport WebSockets verwendet werden, um die Latenzzeiten zu reduzieren und die Zuverlässigkeit und Sicherheit zu erhöhen. SignalR verwendet Long Polling, wenn WebSockets nicht verfügbar oder die App explizit für die Verwendung von Long Polling konfiguriert ist. Konfigurieren Sie die App bei Bereitstellung für Azure App Service in den Einstellungen für den Dienst im Azure-Portal für die Verwendung von WebSockets. Weitere Informationen zum Konfigurieren der App für Azure App Service finden Sie in den Richtlinien für die SignalR-Veröffentlichung.

Eine Konsolenwarnung wird angezeigt, wenn Long Polling verwendet wird:

Fehler beim Herstellen einer Verbindung über WebSockets mithilfe des Long Polling-Fallbacktransports. Möglicherweise wird die Verbindung durch ein VPN oder einen Proxy blockiert.

Globale Bereitstellungs- und Verbindungsfehler

Empfehlungen für globale Bereitstellungen in regionalen Rechenzentren:

  • Stellen Sie die App in den Regionen bereit, in denen sich die meisten Benutzer befinden.
  • Berücksichtigen Sie bei kontinentübergreifendem Datenverkehr die erhöhte Latenz. Informationen zum Steuern der Darstellung der Benutzeroberfläche für die erneute Verbindung finden Sie unter Leitfaden zu ASP.NET Core BlazorSignalR.
  • Verwenden Sie für Azure-Hosting den Azure-Dienst SignalR.

Azure SignalR Service

Für Blazor-Web-Apps, die interaktives serverseitiges Rendering übernehmen, sollten Sie Azure SignalR Service verwenden. Der Dienst arbeitet mit dem Blazor-Hub der App zusammen, um diese für eine große Anzahl gleichzeitiger SignalR-Verbindungen hochskalieren zu können. Außerdem tragen die globale Reichweite und die Hochleistungsrechenzentren von Service erheblich zur Verringerung der geografiebedingten Latenz bei. Wenn Ihre Hostingumgebung diese Punkte bereits abdeckt, ist die Verwendung von Azure SignalR Service nicht erforderlich.

Erwägen Sie die Verwendung von Azure SignalR Service. Der Dienst arbeitet mit dem Blazor-Hub der App zusammen, um diese für eine große Anzahl gleichzeitiger SignalR-Verbindungen hochskalieren zu können. Außerdem tragen die globale Reichweite und die Hochleistungsrechenzentren von Service erheblich zur Verringerung der geografiebedingten Latenz bei. Wenn Ihre Hostingumgebung diese Punkte bereits abdeckt, ist die Verwendung von Azure SignalR Service nicht erforderlich.

Wichtig

Wenn WebSockets deaktiviert sind, simuliert Azure App Service eine Echtzeitverbindung mithilfe von langen HTTP-Abrufen. Das lange Abrufen über HTTP ist deutlich langsamer als die Ausführung mit aktiviertem WebSockets, das keinen Abruf verwendet, um eine Clientserververbindung zu simulieren. Für den Fall, dass Long Polling erforderlich ist, müssen Sie eventuell das maximale Abfrageintervall (MaxPollIntervalInSeconds) konfigurieren. Es legt das maximal zulässige Abfrageintervall für Long Polling-Verbindungen in Azure SignalR Service fest, falls der Dienst jemals von WebSockets auf Long Polling zurückwechselt. Wenn die nächste Abfrageanforderung nicht innerhalb von MaxPollIntervalInSeconds eingeht, bereinigt Azure SignalR Service die Clientverbindung. Beachten Sie, dass Azure SignalR Service Verbindungen auch bereinigt, wenn die Größe des zwischengespeicherten Schreibpuffers größer als 1 MB ist, um die Leistung des Diensts sicherzustellen. Der Standardwert für MaxPollIntervalInSeconds ist 5 Sekunden. Die Einstellung ist auf 1–300 Sekunden begrenzt.

Es wird empfohlen, Websockets für serverseitige Blazor-Apps zu verwenden, die in Azure App Service bereitgestellt werden. In Azure SignalR Service werden Websockets standardmäßig verwendet. Wenn die App den SignalR-Dienst von Azure nicht verwendet, finden Sie weitere Informationen unter Veröffentlichen einer ASP.NET Core-SignalR-App bei Azure App Service.

Weitere Informationen finden Sie in folgenden Quellen:

Konfiguration

Wenn Sie eine App für Azure SignalR Service konfigurieren, muss die App persistente Sitzungen unterstützen, in denen Clients beim Prerendering wieder zurück zum gleichen Server geleitet werden. Die ServerStickyMode-Option oder der Konfigurationswert ist auf Required festgelegt. In der Regel erstellt eine App die Konfiguration mithilfe von einem der folgenden Ansätze:

  • Program.cs:

    builder.Services.AddSignalR().AddAzureSignalR(options =>
    {
        options.ServerStickyMode = 
            Microsoft.Azure.SignalR.ServerStickyMode.Required;
    });
    
  • Konfiguration (verwenden Sie einen der folgenden Ansätze):

    • In appsettings.json:

      "Azure:SignalR:ServerStickyMode": "Required"
      
    • Konfiguration>Anwendungseinstellungen des App-Diensts im Azure-Portal (Name: Azure__SignalR__ServerStickyMode, Wert: Required). Dieser Ansatz wird für die App automatisch übernommen, wenn Sie Azure SignalR Service bereitstellen.

Hinweis

Der folgende Fehler wird von einer App ausgelöst, die keine persistenten Sitzungen für Azure SignalR Service aktiviert hat:

blazor.server.js:1 Uncaught (in promise) Error: Invocation canceled due to the underlying connection being closed. (Nicht abgefangen (in Zusage) Fehler: Der Aufruf wurde abgebrochen, weil die zugrunde liegende Verbindung beendet wurde.)

Bereitstellen von Azure SignalR Service

So stellen Sie Azure SignalR Service für eine App in Visual Studio bereit:

  1. Erstellen Sie ein Veröffentlichungsprofil für Azure-Apps in Visual Studio für die -App.
  2. Fügen Sie dem Profil die Azure SignalR Service-Abhängigkeit hinzu. Wenn das Azure-Abonnement nicht über eine bereits vorhandene Azure SignalR Service-Instanz verfügt, die der App zugewiesen werden soll, wählen Sie Neue Azure SignalR Service-Instanz erstellen aus, um eine neue Dienstinstanz bereitzustellen.
  3. Veröffentlichen Sie die App in Azure.

Durch die Bereitstellung von Azure SignalR Service in Visual Studio werden automatisch persistente Sitzungen aktiviert, und die SignalR-Verbindungszeichenfolge wird der Konfiguration des App-Diensts hinzugefügt.

Skalierbarkeit für Azure Container Apps

Die Skalierung von serverseitigen Blazor-Apps in Azure Container Apps erfordert besondere Überlegungen zusätzlich zur Verwendung von Azure SignalR Service. Aufgrund der Art und Weise, wie das Routing von Anforderungen gehandhabt wird, muss der ASP.NET Core-Datenschutzdienst so konfiguriert werden, dass Schlüssel an einem zentralen Ort aufbewahrt 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 Datenschutzdienst verwendet die Schlüssel zum Deserialisieren von Razor-Komponenten.

Hinweis

Eine eingehendere Erkundung dieses Szenarios und der Skalierung von Container-Apps finden Sie unter Skalieren von ASP.NET Core-Apps in Azure. In diesem Tutorial wird erklärt, wie Sie die Dienste erstellen und integrieren, die für das Hosten von Apps in Azure Container Apps erforderlich sind. In diesem Abschnitt werden auch die grundlegenden Schritte beschrieben.

  1. Wenn Sie den Datenschutzdienst für Azure Blob Storage und Azure Key Vault konfigurieren möchten, verweisen Sie auf die folgenden NuGet-Pakete:

    Hinweis

    Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

  2. Aktualisieren Sie Program.cs mit 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();
    

    Dank der vorherigen Änderungen kann die App den Datenschutz mithilfe einer zentralen, skalierbaren Architektur verwalten. DefaultAzureCredential ermittelt die verwaltete Identität der Container-App, nachdem der Code in Azure bereitgestellt wurde, und nutzt sie zum Herstellen einer Verbindung mit Blobspeicher und dem Schlüsseltresor der App.

  3. 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:

    1. Navigieren Sie im Azure-Portal zur Übersichtsseite der Container-App.
    2. Wählen Sie im linken Navigationsbereich Dienstconnector aus.
    3. Wählen Sie im oberen Navigationsbereich + Erstellen aus.
    4. Geben Sie im Flyoutmenü Verbindung erstellen die folgenden Werte ein:
      • Container: Wählen Sie die Container-App aus, die Sie erstellt haben, um Ihre App zu hosten.
      • Diensttyp: Wählen Sie Blob Storage aus.
      • Abonnement: Wählen Sie das Abonnement aus, das als Besitzer der Container-App fungiert.
      • Verbindungsname: Geben Sie den Namen scalablerazorstorage ein.
      • Clienttyp: Wählen Sie .NET und anschließend Weiter aus.
    5. Wählen Sie Systemseitig zugewiesene verwaltete Identität und dann Weiter aus.
    6. Übernehmen Sie die Standardnetzwerkeinstellungen, und wählen Sie Weiter aus.
    7. 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.

Azure App Service ohne Azure SignalR Service

Das Hosten einer Blazor-Web-App, die interaktives serverseitiges Rendering in Azure App Service verwendet, erfordert eine Konfiguration für die ARR-Affinität (Application Request Routing, Routing von Anwendungsanforderungen) und WebSockets. App Service sollte auch global verteilt werden, um die Benutzeroberflächenlatenz zu verringern. Die Verwendung von Azure SignalR Service beim Hosten in Azure App Service ist nicht erforderlich.

Wenn eine Blazor Server-App in Azure App Service gehostet wird, müssen die ARR-Affinität (Application Request Routing) und WebSockets konfiguriert werden. App Service sollte auch global verteilt werden, um die Benutzeroberflächenlatenz zu verringern. Die Verwendung von Azure SignalR Service beim Hosten in Azure App Service ist nicht erforderlich.

Berücksichtigen Sie beim Konfigurieren der App Folgendes:

IIS

Aktivieren Sie bei Verwendung von IIS Folgendes:

Kubernetes

Erstellen Sie eine Eingangsdefinition mithilfe der folgenden Kubernetes-Anmerkungen für persistente Sitzungen:

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 location-Pfad 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:

Linux mit Apache

Konfigurieren Sie ProxyPass für den HTTP- und WebSockets-Datenverkehr, um eine Blazor-App hinter Apache unter Linux zu hosten.

Im folgenden Beispiel:

  • Der Kestrel-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. Fehlerbeispiele:

  • Firefox kann unter ws://the-domain-name.tld/_blazor?id=XXX keine Verbindung mit dem Server herstellen.
  • Fehler: "Failed to start the transport 'WebSockets'" (Beim Starten des Transports „Websockets“ ist ein Fehler aufgetreten): Fehler: "There was an error with the transport." (Beim Transport ist ein Fehler aufgetreten.)
  • 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: "Unable to connect to the server with any of the available transports." (Es kann mit keinem der verfügbaren Transporte eine Verbindung mit dem Server hergestellt werden.) "WebSockets failed" (WebSockets-Fehler)
  • Fehler: "Cannot send data if the connection is not in the 'Connected' State." (Es können keine Daten gesendet werden, wenn die Verbindung nicht den Zustand „Verbunden“ aufweist.)

Weitere Informationen und einen Leitfaden zur Konfiguration finden Sie in den folgenden Ressourcen:

Messen der Netzwerklatenz

Mit JS-Interop kann die Netzwerklatenz gemessen werden, 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

@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ächenerfahrung wird eine dauerhafte Benutzeroberflächenlatenz von höchstens 250 ms empfohlen.

Speicherverwaltung

Auf dem Server wird für jede Benutzersitzung eine neue Verbindung erstellt. Jede Benutzersitzung entspricht dem Rendern eines einzelnen Dokuments im Browser. Sie erstellen mehrere Registerkarten beispielsweise mehrere Sitzungen.

Blazor unterhält eine konstante Verbindung (als Leitung bezeichnet) mit dem Browser, der die Sitzung initiiert hat. Verbindungen können jederzeit aus verschiedenen Gründen unterbrochen werden, z. B. wenn die Netzwerkkonnektivität von Benutzer*innen verloren geht oder diese den Browser abrupt schließen. Wenn eine Verbindung unterbrochen wird, verfügt Blazor über einen Wiederherstellungsmechanismus, der eine begrenzte Anzahl von Leitungen in einem „getrennten“ Pool platziert, sodass Clients eine begrenzte Zeit haben, um die Verbindung und die Sitzung wiederherzustellen (Standard: 3 Minuten).

Nach diesem Zeitpunkt gibt Blazor die Verbindung frei und verwirft die Sitzung. Ab diesem Zeitpunkt ist die Leitung für die Garbage Collection (GC) freigegeben und kann beansprucht werden, wenn eine Sammlung für die GC-Generierung der Leitung ausgelöst wird. Ein wichtiger Aspekt dabei ist, dass Leitungen eine lange Lebensdauer haben, sodass die meisten Objekte, die aus der Leitung stammen, schließlich Gen2 erreichen. Daher werden diese Objekte möglicherweise erst freigegeben, wenn eine Gen2-Sammlung erfolgt.

Messen des allgemeinen Speicherverbrauchs

Voraussetzungen:

  • Die App muss mit der Releasekonfiguration veröffentlicht werden. Messungen der Debugkonfiguration sind nicht relevant, da der generierte Code nicht repräsentativ für den Code ist, der für eine Produktionsbereitstellung verwendet wird.
  • Die App muss ohne angefügten Debugger ausgeführt werden, da sich dieser ebenfalls auf das Verhalten der App auswirken und die Ergebnisse beeinträchtigen kann. Starten Sie die App in Visual Studio ohne Debugging, indem Sie auf der Menüleiste Debuggen>Ohne Debuggen starten auswählen oder die Tastenkombination STRG+F5 drücken.
  • Betrachten Sie die verschiedenen Speichertypen, um zu verstehen, wie viel Arbeitsspeicher tatsächlich von .NET verwendet wird. Im Allgemeinen überprüfen Entwickler*innen die Nutzung des App-Arbeitsspeichers unter Windows-Betriebssystemen im Task-Manager. Dort wird aber in der Regel die Obergrenze des tatsächlich verwendeten Arbeitsspeichers angezeigt. Weitere Informationen finden Sie in den folgenden Artikeln:

Speicherverbrauch angewandt auf Blazor

Der von Blazor verwendete Speicher wird wie folgt berechnet:

(Aktive Leitungen × Speicher pro Leitung) + (Getrennte Leitungen × Speicher pro Leitung)

Die Menge des Arbeitsspeichers, den eine Leitung nutzt, und die maximale Anzahl potenziell aktiver Leitungen, die eine App verwalten kann, hängen sehr stark davon ab, wie die App geschrieben ist. Die maximale Anzahl möglicher aktiver Leitungen wird grob wie folgt beschrieben:

Maximal verfügbarer Speicher / Speicher pro Leitung = Maximale Anzahl potenziell aktive Leitungen

Damit in Blazor ein Arbeitsspeicherverlust auftritt, muss Folgendes zutreffen:

  • Der Speicher muss vom Framework und nicht von der App zugewiesen werden. Wenn Sie ein Array mit 1 GB in der App zuweisen, muss die App die Bereinigung des Arrays verwalten.
  • Der Speicher darf nicht aktiv genutzt werden. Das bedeutet, dass die Leitung nicht aktiv ist und aus dem Cache für getrennte Leitungen entfernt wurde. Wenn die maximale Anzahl aktiver Leitungen ausgeführt wird, ist Arbeitsspeichermangel ein Skalierungsproblem, kein Arbeitsspeicherverlust.
  • Eine Garbage Collection (GC) für die GC-Generation der Leitung wurde ausgeführt, aber der Garbage Collector konnte die Leitung nicht beanspruchen, da ein anderes Objekt im Framework einen starken Verweis auf die Leitung enthält.

In anderen Fällen tritt kein Arbeitsspeicherverlust auf. Wenn die Leitung aktiv ist (verbunden oder getrennt), wird sie weiterhin verwendet.

Wenn eine Sammlung für die GC-Generation der Leitung nicht ausgeführt wurde, wird der Speicher nicht freigegeben, da der Garbage Collector den Speicher zu diesem Zeitpunkt nicht freigeben muss.

Wenn eine Sammlung für eine GC-Generation ausgeführt wird und die Leitung freigibt, müssen Sie den Arbeitsspeicher mit den GC-Statistiken und nicht anhand des Prozesses überprüfen, da .NET möglicherweise entscheidet, den virtuellen Arbeitsspeicher aktiv zu halten.

Wenn der Arbeitsspeicher nicht freigegeben wird, müssen Sie eine Leitung finden, die weder aktiv noch getrennt ist und die von einem anderen Objekt im Framework stammt. In jedem anderen Fall besteht ein App-Problem im Entwicklungscode, wenn kein Speicher freigegeben werden kann.

Reduzieren des Arbeitsspeicherverbrauchs

Wenden Sie eine der folgenden Strategien an, um den Arbeitsspeicherverbrauch einer App zu reduzieren:

  • Begrenzen Sie den gesamte vom .NET-Prozess genutzten Arbeitsspeicher. Weitere Informationen finden Sie unter Runtimekonfigurationsoptionen für Garbage Collection.
  • Reduzieren Sie die Anzahl der getrennten Leitungen.
  • Reduzieren Sie die Zeit, die sich eine Leitung im getrennten Zustand befinden darf.
  • Lösen Sie manuell eine Garbage Collection aus, um während einer Downtime eine Sammlung auszuführen.
  • Konfigurieren Sie die Garbage Collection nicht im anstelle des Servermodus, sondern im Arbeitsstationsmodus, der die Garbage Collection aggressiv auslöst.

Heapgröße für einige Browser für mobile Geräte

Beim Erstellen einer Blazor-App, die auf dem Client ausgeführt wird und auf Browser für mobile Geräte ausgerichtet ist (insbesondere Safari unter iOS), kann es erforderlich sein, den maximalen Arbeitsspeicher für die App mit der MSBuild-Eigenschaft EmccMaximumHeapSize zu reduzieren. Weitere Informationen finden Sie unter Hosten und Bereitstellen von Blazor WebAssembly in ASP.NET Core.

Zusätzliche Aktionen und Überlegungen

  • Erfassen Sie ein Speicherabbild des Prozesses, wenn der Arbeitsspeicherbedarf hoch ist, und identifizieren Sie die Objekte, die den meisten Arbeitsspeicher beanspruchen, und ihren Ursprung (von wo aus darauf verwiesen wird).
  • Sie können die Statistiken zur Funktionsweise des Arbeitsspeichers in Ihrer App prüfen dotnet-counters. Weitere Informationen finden Sie unter Untersuchen von Leistungsindikatoren (dotnet-counters).
  • Selbst wenn ein GC ausgelöst wird, hält .NET den Speicher gedrückt, anstatt es sofort an das Betriebssystem zurückzugeben, da es wahrscheinlich den Speicher in naher Zukunft wiederverwenden wird. Dies vermeidet, dass der Speicher ständig aktiviert und deaktiviert wird, was teuer ist. Dies sehen Sie wiedergespiegelt, wenn Siedotnet-counters verwenden, da Sie dann feststellen werden, dass GCs auftreten und das Volumen des verwendeten Arbeitsspeichers auf 0 (Null) reduziert wird, Was Sie aber nicht sehen werden, ist, dass der Arbeitssatzzähler zurückgeht, was ein Zeichen dafür ist, dass .NET den Speicher hält, um ihn wiederzuverwenden. Weitere Informationen zu Einstellungen in der Projektdatei (.csproj) zum Steuern dieses Verhaltens finden Sie unter Runtimekonfigurationsoptionen für Garbage Collection.
  • Der Server GC löst keine Garbage Collections aus, es sei den, dass er feststellt, dass dies unbedingt erforderlich ist, um das Einfrieren Ihrer App zu vermeiden und davon ausgeht, dass Ihre App das einzige Element ist, das auf dem Computer ausgeführt wird, so dass er den gesamten Arbeitsspeicher im System verwenden kann. Wenn das System über 50 GB verfügt, versucht der Garbage Collector, die vollen 50 GB des verfügbaren Arbeitsspeichers zu verwenden, bevor er eine Gen2-Sammlung auslöst.
  • Weitere Informationen zur Konfiguration der Aufbewahrung von getrennten Leitungen finden Sie im Leitfaden zu ASP.NET Core BlazorSignalR.

Messen des Arbeitsspeichers

  • Veröffentlichen Sie die App in der Releasekonfiguration.
  • Führen Sie eine veröffentlichte Version der App aus.
  • Fügen Sie keinen Debugger an die ausgeführte App an.
  • Löst eine Aktivierung eine Gen 2 erzwungene Komprimierungssammlung aus (GC.Collect(2, GCCollectionMode.Aggressive | GCCollectionMode.Forced, blocking: true, compacting: true))den Speicher freigeben?
  • Prüfen Sie, ob Ihre App Objekte dem Heap für große Objekte zuordnet.
  • Testen Sie die Speichererweiterung, nachdem die App mit Anforderungen und Verarbeitung aufgewärmt wurde? In der Regel gibt es Caches, die beschrieben werden, wenn der Code zum ersten Mal ausgeführt wird, womit dem Speicherbedarf der App eine konstante Menge an Arbeitsspeicher hinzugefügt wird.