Teilen über


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.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der Supportrichtlinie für .NET und .NET Core. 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 erklärt, 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.

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.

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

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.
  • Erwägen Sie die Nutzung des Azure SignalR Service.

Azure App Service

Die Bereitstellung auf Azure App Service erfordert eine Konfiguration für WebSockets und Sitzungsaffinität, auch als Routing von Anwendungsanforderungen (ARR)-Affinität bezeichnet.

Hinweis

Eine Blazor-App auf Azure App Service erfordert keinen Azure SignalR Service.

Aktivieren Sie Folgendes für die Registrierung der App in Azure App Service:

  • WebSockets, damit der WebSockets-Transport funktioniert. Die Standardeinstellung ist Aus.
  • Sitzungsaffinität, um Anfragen von Benutzenden an dieselbe App Service-Instanz zurückzusenden. Die Standardeinstellung ist Ein.
  1. Navigieren Sie im Azure-Portal in App Services zur Web-App.
  2. Öffnen Sie Einstellungen>Konfiguration.
  3. Legen Sie WebSockets auf Ein fest.
  4. Stellen Sie sicher, dass Sitzungsaffinität auf Ein festgelegt ist.

Azure SignalR Service

Der optionale Azure SignalR Service arbeitet mit dem Hub der App SignalR zusammen, um eine serverseitige App auf eine große Anzahl gleichzeitiger Verbindungen zu skalieren. 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 Dienst- oder Azure-Container-Apps gehostet werden, aber in anderen Hostingumgebungen hilfreich sein können:

  • Um das Aufskalieren der Verbindung zu erleichtern.
  • Um die globale Verteilung zu gewährleisten.

Hinweis

Zustandsbehaftete erneute Verbindung (WithStatefulReconnect) wurde mit .NET 8 veröffentlicht, wird jedoch für den Azure SignalR-Dienst derzeit nicht unterstützt. Weitere Informationen finden Sie unter Stateful Reconnect Support? (Azure/azure-signalr Nr. 1878).

Falls die App lange Abrufe verwendet oder auf lange Abrufe anstelle von WebSockets zurückgreift, müssen Sie möglicherweise das maximale Abfrageintervall (MaxPollIntervalInSeconds, Standard: 5 Sekunden, Grenzwert: 1–300 Sekunden) konfigurieren, das das maximal zulässige Abfrageintervall für lange Abrufverbindungen im Azure SignalR Service definiert. Wenn die nächste Abfrageanforderung nicht innerhalb des maximalen Abrufintervalls eingeht, schließt der Dienst die Clientverbindung.

Weitere Informationen zum Hinzufügen des Dienstes 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 Container Apps

Eine tiefergehende Erkundung der Skalierung serverseitiger Blazor-Apps im Azure Container Apps-Dienst finden Sie unter Skalieren 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. Konfigurieren Sie den Azure Container Apps-Dienst für die Sitzungsaffinität, indem Sie die Anleitung in Sitzungsaffinität in Azure-Container-Apps (Azure-Dokumentation) befolgen.

  2. Der ASP.NET Core-Datenschutzdienst muss 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. 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.

  3. 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 Datenschutzdienst mithilfe einer zentralen, skalierbaren Architektur verwalten. DefaultAzureCredential ermittelt die Containerapp, die identity nach der Bereitstellung des Codes in Azure verwaltet wird, und verwendet sie, um eine Verbindung zum Blobspeicher und zum Schlüsseltresor der App herzustellen.

  4. Führen Sie die folgenden Schritte aus, um die verwaltete identity 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 identity und wählen Sie Weiter.
    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.

IIS

Aktivieren Sie bei Verwendung von IIS Folgendes:

Weitere Informationen finden Sie in den Anleitungen und externen IIS-Ressourcen-Querverknüpfungen in Veröffentlichung einer ASP.NET Core-App in IIS.

Kubernetes

Erstellen Sie eine Ingress-Definition mit den folgenden Kubernetes-Kommentaren für 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 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

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