Hostowanie i wdrażanie aplikacji po stronie Blazor serwera

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

W tym artykule wyjaśniono, jak hostować i wdrażać aplikacje po stronie Blazor serwera (Blazor Web Apps i Blazor Server aplikacje) przy użyciu platformy ASP.NET Core.

Wartości konfiguracji hosta

Aplikacje po stronie Blazor serwera mogą akceptować ogólne wartości konfiguracji hosta.

Wdrożenie

Użycie modelu Blazor hostingu po stronie serwera jest wykonywane na serwerze z poziomu aplikacji ASP.NET Core. Aktualizacje interfejsu użytkownika, obsługa zdarzeń i wywołania języka JavaScript są obsługiwane za pośrednictwem SignalR połączenia.

Wymagany jest serwer internetowy obsługujący aplikację ASP.NET Core. Program Visual Studio zawiera szablon projektu aplikacji po stronie serwera. Aby uzyskać więcej informacji na Blazor temat szablonów projektów, zobacz ASP.NET Core project structure (Struktura projektu ASP.NET CoreBlazor).

Opublikuj aplikację w konfiguracji wydania i wdróż zawartość bin/Release/{TARGET FRAMEWORK}/publish folderu, w {TARGET FRAMEWORK} którym symbol zastępczy jest platformą docelową.

Skalowalność

Biorąc pod uwagę skalowalność pojedynczego serwera (skalowanie w górę), pamięć dostępna dla aplikacji jest prawdopodobnie pierwszym zasobem, który aplikacja wyczerpała w miarę wzrostu zapotrzebowania użytkownika. Dostępna pamięć na serwerze ma wpływ na:

  • Liczba aktywnych obwodów, które może obsługiwać serwer.
  • Opóźnienie interfejsu użytkownika na kliencie.

Aby uzyskać wskazówki dotyczące tworzenia bezpiecznych i skalowalnych aplikacji po stronie Blazor serwera, zobacz następujące zasoby:

Każdy obwód używa około 250 KB pamięci dla minimalnej aplikacji hello world stylu. Rozmiar obwodu zależy od kodu aplikacji i wymagań dotyczących konserwacji stanu skojarzonych z każdym składnikiem. Zalecamy mierzenie zapotrzebowania na zasoby podczas opracowywania aplikacji i infrastruktury, ale następujący punkt odniesienia może stanowić punkt początkowy planowania celu wdrożenia: Jeśli oczekujesz, że aplikacja będzie obsługiwać 5000 współbieżnych użytkowników, rozważ budżetowanie co najmniej 1,3 GB pamięci serwera do aplikacji (lub ok. 273 KB na użytkownika).

Konfiguracja widoku SignalR

SignalRWarunki hostingu i skalowania mają zastosowanie do Blazor aplikacji korzystających z programu SignalR.

Aby uzyskać więcej informacji na SignalR temat aplikacji, w Blazor tym wskazówek dotyczących konfiguracji, zobacz wskazówki dotyczące platformy ASP.NET CoreBlazorSignalR.

Transporty

Blazor działa najlepiej w przypadku korzystania z obiektów WebSocket jako SignalR transportu ze względu na mniejsze opóźnienia, lepszą niezawodność i lepsze zabezpieczenia. Długie sondowanie jest używane, SignalR gdy zestawy WebSocket nie są dostępne lub gdy aplikacja jest jawnie skonfigurowana do korzystania z długiego sondowania. Podczas wdrażania w usłudze aplikacja systemu Azure skonfiguruj aplikację tak, aby korzystała z obiektów WebSocket w ustawieniach witryny Azure Portal dla usługi. Aby uzyskać szczegółowe informacje na temat konfigurowania aplikacji dla usługi aplikacja systemu Azure Service, zobacz SignalR wytyczne dotyczące publikowania.

Jeśli jest używane długie sondowanie, zostanie wyświetlone ostrzeżenie konsoli:

Nie można nawiązać połączenia za pośrednictwem obiektów WebSocket przy użyciu transportu rezerwowego Long Polling. Może to być spowodowane blokowaniem połączenia przez sieć VPN lub serwer proxy.

Globalne błędy wdrażania i połączenia

Rekomendacje na potrzeby wdrożeń globalnych w centrach danych geograficznych:

  • Wdróż aplikację w regionach, w których większość użytkowników mieszka.
  • Weź pod uwagę zwiększone opóźnienie ruchu na różnych kontynentach. Aby kontrolować wygląd interfejsu użytkownika ponownego łączenia, zobacz wskazówki ASP.NET CoreBlazorSignalR.
  • W przypadku hostingu platformy Azure użyj usługi platformy AzureSignalR.

Usługa platformy Azure SignalR

W przypadku Blazor usługi Web Apps, które przyjmują interaktywne renderowanie po stronie serwera, rozważ użycie usługi platformy AzureSignalR. Usługa działa w połączeniu z centrum aplikacji Blazor w celu skalowania w górę do dużej liczby współbieżnych SignalR połączeń. Ponadto globalne zasięg usługi i centra danych o wysokiej wydajności znacznie pomagają zmniejszyć opóźnienia ze względu na lokalizację geograficzną. Jeśli środowisko hostingu już obsługuje te problemy, korzystanie z usługi platformy Azure SignalR nie jest konieczne.

Rozważ użycie usługi platformy AzureSignalR, która działa w połączeniu z centrum aplikacji Blazor w celu skalowania w górę do dużej liczby współbieżnych SignalR połączeń. Ponadto globalne zasięg usługi i centra danych o wysokiej wydajności znacznie pomagają zmniejszyć opóźnienia ze względu na lokalizację geograficzną. Jeśli środowisko hostingu już obsługuje te problemy, korzystanie z usługi platformy Azure SignalR nie jest konieczne.

Ważne

Gdy zestawy WebSocket są wyłączone, usługa aplikacja systemu Azure symuluje połączenie w czasie rzeczywistym przy użyciu długiego sondowania HTTP. Sondowanie długie protokołu HTTP jest znacznie wolniejsze niż uruchamianie z włączonymi protokołami WebSocket, co nie używa sondowania do symulowania połączenia klient-serwer. W przypadku, gdy należy użyć długiego sondowania, może być konieczne skonfigurowanie maksymalnego interwału sondowania (MaxPollIntervalInSeconds), który definiuje maksymalny interwał sondowania dozwolony dla długich połączeń sondowania w usłudze AzureSignalR, jeśli usługa kiedykolwiek wróci z obiektów WebSocket do long polling. Jeśli następne żądanie sondowania nie jest dostępne w ramach MaxPollIntervalInSecondsusługi platformy Azure, usługa platformy Azure SignalR czyści połączenie klienta. Należy pamiętać, że usługa platformy Azure SignalR czyści również połączenia, gdy buforowane oczekujące na zapisanie rozmiaru buforu jest większe niż 1 MB, aby zapewnić wydajność usługi. Wartość domyślna to MaxPollIntervalInSeconds 5 sekund. Ustawienie jest ograniczone do 1–300 sekund.

Zalecamy używanie obiektów WebSocket dla aplikacji po stronie Blazor serwera wdrożonych w usłudze aplikacja systemu Azure Service. Usługa platformy Azure SignalR domyślnie używa obiektów WebSocket. Jeśli aplikacja nie korzysta z usługi Platformy AzureSignalR, zobacz Publikowanie aplikacji ASP.NET Core SignalR w celu aplikacja systemu Azure Service.

Aby uzyskać więcej informacji, zobacz:

Konfigurowanie

Aby skonfigurować aplikację dla usługi platformy Azure SignalR , aplikacja musi obsługiwać sesje sticky, gdzie klienci są przekierowywani z powrotem do tego samego serwera podczas prerenderingu. Opcja ServerStickyMode lub wartość konfiguracji jest ustawiona na Required. Zazwyczaj aplikacja tworzy konfigurację przy użyciu jednego z następujących podejść:

  • Program.cs:

    builder.Services.AddSignalR().AddAzureSignalR(options =>
    {
        options.ServerStickyMode = 
            Microsoft.Azure.SignalR.ServerStickyMode.Required;
    });
    
  • Konfiguracja (użyj jednej z następujących metod):

    • W pliku appsettings.json:

      "Azure:SignalR:ServerStickyMode": "Required"
      
    • Ustawienia aplikacji konfiguracji>usługi App Service w witrynie Azure Portal (nazwa: Azure__SignalR__ServerStickyMode, wartość: ). Required To podejście jest przyjęte automatycznie dla aplikacji, jeśli aprowizujesz usługę platformy AzureSignalR.

Uwaga

Następujący błąd jest zgłaszany przez aplikację, która nie włączyła sesji sticky dla usługi platformy Azure SignalR :

blazor.server.js:1 Uncaught (w obietnicy) Błąd: wywołanie anulowane z powodu zamknięcia połączenia bazowego.

Aprowizuj usługę platformy Azure SignalR

Aby aprowizować usługę platformy Azure SignalR dla aplikacji w programie Visual Studio:

  1. Utwórz profil publikowania aplikacja systemu Azure w programie Visual Studio dla aplikacji.
  2. Dodaj zależność usługi platformy Azure SignalR do profilu. Jeśli subskrypcja platformy Azure nie ma wstępnie istniejącego wystąpienia usługi platformy Azure SignalR do przypisania do aplikacji, wybierz pozycję Utwórz nowe wystąpienie usługi platformy Azure SignalR , aby aprowizować nowe wystąpienie usługi.
  3. Publikowanie aplikacji na platformie Azure

Aprowizowanie usługi platformy Azure SignalR w programie Visual Studio automatycznie włącza trwałe sesje i dodaje SignalR parametry połączenia do konfiguracji usługi App Service.

Skalowalność w usłudze Azure Container Apps

Skalowanie aplikacji po stronie Blazor serwera w usłudze Azure Container Apps wymaga konkretnych zagadnień oprócz korzystania z usługi platformy AzureSignalR. Ze względu na sposób obsługi routingu żądań usługa ochrony danych ASP.NET Core musi być skonfigurowana do utrwalania kluczy w centralnej lokalizacji, do których mogą uzyskiwać dostęp wszystkie wystąpienia kontenerów. Klucze mogą być przechowywane w usłudze Azure Blob Storage i chronione za pomocą usługi Azure Key Vault. Usługa ochrony danych używa kluczy do deserializacji Razor składników.

Uwaga

Aby dowiedzieć się więcej na temat tego scenariusza i skalowania aplikacji kontenerów, zobacz Scaling ASP.NET Core Apps on Azure (Skalowanie ASP.NET Core Apps na platformie Azure). W tym samouczku wyjaśniono, jak tworzyć i integrować usługi wymagane do hostowania aplikacji w usłudze Azure Container Apps. Podstawowe kroki są również dostępne w tej sekcji.

  1. Aby skonfigurować usługę ochrony danych do korzystania z usług Azure Blob Storage i Azure Key Vault, zapoznaj się z następującymi pakietami NuGet:

    Uwaga

    Aby uzyskać instrukcje dodawania pakietów do aplikacji .NET, zobacz artykuły w sekcji Instalowanie pakietów i zarządzanie nimi w temacie Przepływ pracy użycia pakietów (dokumentacja programu NuGet). Sprawdź prawidłowe wersje pakietów pod adresem NuGet.org.

  2. Zaktualizuj Program.cs za pomocą następującego wyróżnionego kodu:

    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();
    

    Powyższe zmiany umożliwiają aplikacji zarządzanie ochroną danych przy użyciu scentralizowanej, skalowalnej architektury. DefaultAzureCredential odnajduje tożsamość zarządzaną aplikacji kontenera po wdrożeniu kodu na platformie Azure i używa go do łączenia się z magazynem obiektów blob i magazynem kluczy aplikacji.

  3. Aby utworzyć tożsamość zarządzaną aplikacji kontenera i udzielić jej dostępu do magazynu obiektów blob i magazynu kluczy, wykonaj następujące kroki:

    1. W witrynie Azure Portal przejdź do strony przeglądu aplikacji kontenera.
    2. Wybierz pozycję Połączenie or usługi w obszarze nawigacji po lewej stronie.
    3. Wybierz pozycję + Utwórz z górnej nawigacji.
    4. W menu wysuwany Tworzenie połączenia wprowadź następujące wartości:
      • Kontener: wybierz utworzoną aplikację kontenera do hostowania aplikacji.
      • Typ usługi: wybierz pozycję Blob Storage.
      • Subskrypcja: wybierz subskrypcję, która jest właścicielem aplikacji kontenera.
      • nazwa Połączenie ion: wprowadź nazwę scalablerazorstorage.
      • Typ klienta: wybierz pozycję .NET , a następnie wybierz przycisk Dalej.
    5. Wybierz pozycję Tożsamość zarządzana przypisana przez system i wybierz przycisk Dalej.
    6. Użyj domyślnych ustawień sieciowych i wybierz pozycję Dalej.
    7. Po zweryfikowaniu ustawień przez platformę Azure wybierz pozycję Utwórz.

    Powtórz powyższe ustawienia dla magazynu kluczy. Wybierz odpowiednią usługę i klucz magazynu kluczy na karcie Podstawy .

usługa aplikacja systemu Azure bez platformy Azure SignalR Usługi

Hostowanie Blazor aplikacji internetowej korzystającej z interaktywnego renderowania po stronie serwera w usłudze aplikacja systemu Azure wymaga konfiguracji koligacji routingu żądań aplikacji (ARR) i obiektów WebSocket. Usługa App Service powinna być również odpowiednio dystrybuowana globalnie, aby zmniejszyć opóźnienie interfejsu użytkownika. Korzystanie z usługi platformy Azure SignalR w przypadku hostowania w usłudze aplikacja systemu Azure nie jest wymagane.

Hostowanie Blazor Server aplikacji w usłudze aplikacja systemu Azure wymaga konfiguracji koligacji routingu żądań aplikacji (ARR) i obiektów WebSocket. Usługa App Service powinna być również odpowiednio dystrybuowana globalnie, aby zmniejszyć opóźnienie interfejsu użytkownika. Korzystanie z usługi platformy Azure SignalR w przypadku hostowania w usłudze aplikacja systemu Azure nie jest wymagane.

Skorzystaj z poniższych wskazówek, aby skonfigurować aplikację:

IIS

W przypadku korzystania z usług IIS włącz:

Aby uzyskać więcej informacji, zobacz wskazówki i zewnętrzne linki do zasobów usług IIS w temacie Publikowanie aplikacji ASP.NET Core w usługach IIS.

Kubernetes

Utwórz definicję ruchu przychodzącego z następującymi adnotacjami Kubernetes dla sesji sticky:

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"

System Linux z serwerem Nginx

Postępuj zgodnie ze wskazówkami dotyczącymi aplikacji ASP.NET Core SignalR z następującymi zmianami:

  • Zmień ścieżkę location z /hubroute (location /hubroute { ... }) na ścieżkę / główną (location / { ... }).
  • Usuń konfigurację buforowania serwera proxy (proxy_buffering off;), ponieważ ustawienie ma zastosowanie tylko do zdarzeń wysłanych przez serwer (SSE), które nie są istotne dla Blazor interakcji między klientem aplikacji a serwerem.

Aby uzyskać więcej informacji i wskazówek dotyczących konfiguracji, zapoznaj się z następującymi zasobami:

System Linux z serwerem Apache

Aby hostować aplikację za platformą Blazor Apache w systemie Linux, skonfiguruj ProxyPass ruch HTTP i WebSocket.

W poniższym przykładzie:

  • Kestrel serwer jest uruchomiony na maszynie hosta.
  • Aplikacja nasłuchuje ruchu na porcie 5000.
ProxyPreserveHost   On
ProxyPassMatch      ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass           /_blazor ws://localhost:5000/_blazor
ProxyPass           / http://localhost:5000/
ProxyPassReverse    / http://localhost:5000/

Włącz następujące moduły:

a2enmod   proxy
a2enmod   proxy_wstunnel

Sprawdź konsolę przeglądarki pod kątem błędów obiektów WebSocket. Przykładowe błędy:

  • Firefox nie może nawiązać połączenia z serwerem pod adresem ws://the-domain-name.tld/_blazor?id=XXX
  • Błąd: Nie można uruchomić transportu "WebSockets": Błąd: Wystąpił błąd podczas transportu.
  • Błąd: Nie można uruchomić transportu "LongPolling": TypeError: this.transport jest niezdefiniowany
  • Błąd: Nie można nawiązać połączenia z serwerem z żadnym z dostępnych transportów. Nie można uruchomić obiektów WebSocket
  • Błąd: Nie można wysłać danych, jeśli połączenie nie znajduje się w stanie "Połączenie".

Aby uzyskać więcej informacji i wskazówek dotyczących konfiguracji, zapoznaj się z następującymi zasobami:

Mierzenie opóźnienia sieci

JS Interop może służyć do mierzenia opóźnienia sieci, jak pokazano w poniższym przykładzie.

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();
        }
    }
}

W przypadku rozsądnego środowiska interfejsu użytkownika zalecamy trwałe opóźnienie interfejsu użytkownika 250 ms lub mniej.

Zarządzanie pamięcią

Na serwerze jest tworzony nowy obwód dla każdej sesji użytkownika. Każda sesja użytkownika odpowiada renderowaniu pojedynczego dokumentu w przeglądarce. Na przykład wiele kart tworzy wiele sesji.

Blazorutrzymuje stałe połączenie z przeglądarką, nazywane obwodem, które zainicjowało sesję. Połączenie ions można utracić w dowolnym momencie z kilku powodów, takich jak utrata łączności sieciowej przez użytkownika lub nagłe zamknięcie przeglądarki. W przypadku utraty połączenia ma mechanizm odzyskiwania, Blazor który umieszcza ograniczoną liczbę obwodów w puli "rozłączone", co daje klientom ograniczony czas ponownego nawiązywania połączenia i ponowne ustanawianie sesji (ustawienie domyślne: 3 minuty).

Po tym czasie Blazor zwalnia obwód i odrzuca sesję. Od tego momentu obwód kwalifikuje się do odzyskiwania pamięci (GC) i jest zgłaszany, gdy zostanie wyzwolona kolekcja dla generowania GC obwodu. Ważnym aspektem, który należy zrozumieć, jest to, że obwody mają długi okres istnienia, co oznacza, że większość obiektów zakorzenionych przez obwód ostatecznie osiągnie gen 2. W związku z tym te obiekty mogą nie być widoczne, dopóki kolekcja 2. generacji nie zostanie zwolniona.

Mierzenie ogólnego użycia pamięci

Wymagania wstępne:

  • Aplikacja musi zostać opublikowana w konfiguracji wydania . Pomiary konfiguracji debugowania nie są istotne, ponieważ wygenerowany kod nie jest reprezentatywny dla kodu używanego do wdrożenia produkcyjnego.
  • Aplikacja musi działać bez dołączonego debugera, ponieważ może to również mieć wpływ na zachowanie aplikacji i zepsuć wyniki. W programie Visual Studio uruchom aplikację bez debugowania, wybierając pozycję Debuguj>start bez debugowania na pasku menu lub Ctrl+F5 za pomocą klawiatury.
  • Rozważ różne typy pamięci, aby zrozumieć, ile pamięci jest rzeczywiście używane przez platformę .NET. Ogólnie rzecz biorąc, deweloperzy sprawdzają użycie pamięci aplikacji w Menedżerze zadań w systemie operacyjnym Windows, który zazwyczaj oferuje górną granicę rzeczywistej pamięci w użyciu. Aby uzyskać więcej informacji, zapoznaj się z następującymi artykułami:

Użycie pamięci zastosowane do Blazor

Obliczamy pamięć używaną przez blazor w następujący sposób:

(Obwody aktywne × pamięci obwodu) + (Odłączone obwody × pamięci obwodu)

Ilość pamięci używanej przez obwód oraz maksymalne potencjalne aktywne obwody, które może obsługiwać aplikacja, zależy w dużej mierze od sposobu pisania aplikacji. Maksymalna liczba możliwych aktywnych obwodów jest w przybliżeniu opisana przez:

Maksymalna ilość dostępnej pamięci / = na obwód maksymalna liczba potencjalnych aktywnych obwodów

Aby wyciek pamięci wystąpił w Blazorpliku , muszą być spełnione następujące warunki:

  • Pamięć musi być przydzielona przez strukturę, a nie aplikację. Jeśli przydzielisz tablicę o pojemności 1 GB w aplikacji, aplikacja musi zarządzać usuwaniem tablicy.
  • Pamięć nie może być aktywnie używana, co oznacza, że obwód nie jest aktywny i został wykluczony z pamięci podręcznej odłączonych obwodów. Jeśli masz uruchomione maksymalne aktywne obwody, brak pamięci jest problemem ze skalowaniem, a nie przeciekiem pamięci.
  • Uruchomiono odzyskiwanie pamięci (GC) dla generacji GC obwodu, ale moduł odśmiecywania pamięci nie był w stanie przejąć obwodu, ponieważ inny obiekt w strukturze posiada silne odwołanie do obwodu.

W innych przypadkach nie ma przecieku pamięci. Jeśli obwód jest aktywny (podłączony lub odłączony), obwód jest nadal używany.

Jeśli kolekcja dla generacji GC obwodu nie zostanie uruchomiona, pamięć nie zostanie zwolniona, ponieważ moduł odśmiecnia pamięci nie musi w tym czasie zwolnić pamięci.

Jeśli kolekcja dla generacji GC jest uruchamiana i zwalnia obwód, musisz zweryfikować pamięć względem statystyk GC, a nie proces, ponieważ platforma .NET może zdecydować się zachować aktywną pamięć wirtualną.

Jeśli pamięć nie zostanie zwolniona, musisz znaleźć obwód, który nie jest aktywny lub odłączony i jest zakorzeniony przez inny obiekt w strukturze. W każdym innym przypadku brak możliwości zwolnienia pamięci to problem z aplikacją w kodzie dewelopera.

Zmniejszanie użycia pamięci

Zastosuj dowolną z następujących strategii, aby zmniejszyć użycie pamięci aplikacji:

  • Ogranicz łączną ilość pamięci używanej przez proces platformy .NET. Aby uzyskać więcej informacji, zobacz Opcje konfiguracji środowiska uruchomieniowego dla odzyskiwania pamięci.
  • Zmniejsz liczbę odłączonych obwodów.
  • Zmniejsz czas, przez jaki obwód może znajdować się w stanie odłączenia.
  • Ręczne wyzwalanie odzyskiwania pamięci w celu wykonania kolekcji w okresach przestojów.
  • Skonfiguruj odzyskiwanie pamięci w trybie stacji roboczej, który agresywnie wyzwala odzyskiwanie pamięci zamiast trybu serwera.

Rozmiar sterty dla niektórych przeglądarek urządzeń przenośnych

Podczas kompilowania aplikacji uruchamianej Blazor na kliencie i docelowych przeglądarkach urządzeń przenośnych, zwłaszcza w przeglądarce Safari w systemie iOS, może być wymagana maksymalna ilość pamięci dla aplikacji z właściwością EmccMaximumHeapSize MSBuild. Aby uzyskać więcej informacji, zobacz Host and deploy ASP.NET Core Blazor WebAssembly.

Dodatkowe działania i zagadnienia

  • Przechwyć zrzut pamięci procesu, gdy zapotrzebowanie na pamięć jest wysokie i zidentyfikuj, że obiekty zajmują najwięcej pamięci i gdzie są one zakorzenione (co zawiera odwołanie do nich).
  • Możesz sprawdzić statystyki dotyczące zachowania pamięci w aplikacji przy użyciu polecenia dotnet-counters. Aby uzyskać więcej informacji, zobacz Badanie liczników wydajności (dotnet-counters).
  • Nawet w przypadku wyzwolenia GC platforma .NET przechowuje w pamięci zamiast natychmiast zwracać ją do systemu operacyjnego, ponieważ prawdopodobnie będzie ponownie używać pamięci w najbliższej przyszłości. Pozwala to uniknąć ciągłego zatwierdzania i dekomunikowania pamięci, co jest kosztowne. Zobaczysz to odzwierciedlone, jeśli używasz dotnet-counters , ponieważ wystąpią kontrolery domeny, a ilość używanej pamięci spadnie do 0 (zero), ale nie zobaczysz spadku licznika zestawu roboczego, co oznacza, że platforma .NET trzyma się w pamięci w celu ponownego użycia. Aby uzyskać więcej informacji na temat ustawień pliku projektu (.csproj), aby kontrolować to zachowanie, zobacz Opcje konfiguracji środowiska uruchomieniowego dla odzyskiwania pamięci.
  • Funkcja GC serwera nie wyzwala odzyskiwania pamięci, dopóki nie określi, że jest to absolutnie konieczne, aby uniknąć zamarzania aplikacji i uważa, że aplikacja jest jedyną rzeczą uruchomioną na maszynie, więc może używać całej pamięci w systemie. Jeśli system ma 50 GB, moduł odśmiecenia pamięci będzie używać pełnej 50 GB dostępnej pamięci, zanim wyzwoli kolekcję 2. generacji.
  • Aby uzyskać informacje na temat konfiguracji przechowywania odłączonego obwodu, zobacz wskazówki dotyczące ASP.NET CoreBlazorSignalR.

Mierzenie pamięci

  • Opublikuj aplikację w konfiguracji wydania.
  • Uruchom opublikowaną wersję aplikacji.
  • Nie dołączaj debugera do uruchomionej aplikacji.
  • Czy wyzwalanie wymuszonej, kompaktowanej kolekcji Gen 2 (GC.Collect(2, GCCollectionMode.Aggressive | GCCollectionMode.Forced, blocking: true, compacting: true)) zwalnia pamięć?
  • Rozważ, czy aplikacja przydziela obiekty na stercie dużych obiektów.
  • Czy testujesz wzrost pamięci po rozgrzaniu aplikacji przy użyciu żądań i przetwarzania? Zazwyczaj istnieją pamięci podręczne, które są wypełniane, gdy kod jest wykonywany po raz pierwszy, co zwiększa stałą ilość pamięci do śladu aplikacji.