Hostování a nasazení aplikací na straně Blazor serveru

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v tématu .NET a .NET Core Zásady podpory. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Tento článek vysvětluje, jak hostovat a nasazovat aplikaceBlazor Web AppBlazor Server na straně Blazor serveru pomocí ASP.NET Core.

Hodnoty konfigurace hostitele

Aplikace na straně Blazor serveru můžou přijímat hodnoty konfigurace obecného hostitele.

Nasazení

Použití modelu Blazor hostování na straně serveru se provádí na serveru z aplikace ASP.NET Core. Aktualizace uživatelského rozhraní, zpracování událostí a volání JavaScriptu SignalR se zpracovávají přes připojení.

Vyžaduje se webový server, který může hostovat aplikaci ASP.NET Core. Visual Studio obsahuje šablonu projektu aplikace na straně serveru. Další informace o Blazor šablonách projektů najdete v tématu ASP.NET Blazor Základní struktura projektu.

Publikujte aplikaci v konfiguraci vydané verze a nasaďte obsah bin/Release/{TARGET FRAMEWORK}/publish složky, kde {TARGET FRAMEWORK} zástupný symbol představuje cílovou architekturu.

Škálovatelnost

Při zvažování škálovatelnosti jednoho serveru (vertikální navýšení kapacity) je paměť dostupná pro aplikaci pravděpodobně prvním prostředkem, který aplikace vyčerpá, protože se zvýší požadavky uživatelů. Dostupná paměť na serveru má vliv na:

  • Počet aktivních okruhů, které může server podporovat.
  • Latence uživatelského rozhraní v klientovi

Pokyny k vytváření zabezpečených a škálovatelných aplikací na straně Blazor serveru najdete v následujících zdrojích informací:

Každý okruh používá přibližně 250 kB paměti pro minimální aplikaci ve stylu Hello World. Velikost okruhu závisí na kódu aplikace a požadavcích na údržbu stavu spojených s jednotlivými komponentami. Doporučujeme měřit požadavky na prostředky během vývoje vaší aplikace a infrastruktury, ale následující směrný plán může být výchozím bodem při plánování cíle nasazení: Pokud očekáváte, že vaše aplikace bude podporovat 5 000 souběžných uživatelů, zvažte rozpočtování alespoň 1,3 GB paměti serveru do aplikace (nebo přibližně 273 kB na uživatele).

Konfigurace SignalR

SignalRPodmínky hostování a škálování se vztahují na Blazor aplikace, které používají SignalR.

Další informace o SignalR Blazor aplikacích, včetně pokynů ke konfiguraci, najdete v ASP.NET základních BlazorSignalR doprovodných materiálech.

Přenosy

Blazor funguje nejlépe při použití protokolu WebSocket jako SignalR přenosu kvůli nižší latenci, lepší spolehlivosti a lepšímu zabezpečení. Dlouhé dotazování se používá SignalR , když webSockets není k dispozici nebo když je aplikace explicitně nakonfigurovaná tak, aby používala dlouhé dotazování.

Pokud se používá dlouhé dotazování, zobrazí se upozornění konzoly:

Připojení přes WebSocket se nezdařilo pomocí náhradního přenosu Long Polling. Příčinou může být blokování připojení vpn nebo proxy serverem.

Globální nasazení a selhání připojení

Doporučení pro globální nasazení do geografických datových center:

  • Nasaďte aplikaci do oblastí, ve kterých se nachází většina uživatelů.
  • Vezměte v úvahu zvýšenou latenci provozu napříč kontinenty. Pokud chcete řídit vzhled uživatelského rozhraní pro opětovné připojení, přečtěte si pokyny pro ASP.NET CoreBlazorSignalR.
  • Zvažte použití služby AzureSignalR.

Azure App Service

Hostování ve službě Aplikace Azure Vyžaduje konfiguraci pro webSockets a spřažení relací, označované také jako spřažení směrování požadavků aplikace (ARR).

Poznámka:

Blazor Aplikace ve službě Aplikace Azure Nevyžaduje službu AzureSignalR.

Pro registraci aplikace ve službě Aplikace Azure Service povolte následující:

  • WebSockety umožňující přenos WebSockets do funkce. Výchozí nastavení je Vypnuto.
  • Spřažení relace pro směrování požadavků od uživatele zpět do stejné instance služby App Service. Výchozí nastavení je Zapnuto.
  1. Na webu Azure Portal přejděte do webové aplikace ve službě App Services.
  2. Otevřete konfiguraci nastavení>.
  3. Nastavte webové sokety na Zapnuto.
  4. Ověřte, že je spřažení relace nastavené na Zapnuto.

Služba Azure SignalR

Volitelná služba Azure SignalR funguje ve spojení s centrem aplikace SignalR pro vertikální navýšení kapacity aplikace na straně serveru na velký počet souběžných připojení. Globální dosah služby a vysoce výkonná datová centra navíc výrazně pomáhají snížit latenci kvůli zeměpisné poloze.

Služba se nevyžaduje pro Blazor aplikace hostované ve službě Aplikace Azure Service nebo Azure Container Apps, ale může být užitečná v jiných hostitelských prostředích:

  • Pro usnadnění horizontálního navýšení kapacity připojení.
  • Zpracování globální distribuce

Poznámka:

Stavové opětovné připojení (WithStatefulReconnect) bylo vydáno s .NET 8, ale v současné době se nepodporuje pro službu Azure SignalR . Další informace najdete v tématu Podpora stavových opětovného připojení? (Azure/azure-signalr č. 1878).

V případě, že aplikace používá dlouhé dotazování nebo se vrátí k dlouhému dotazování místo webSocketů, možná budete muset nakonfigurovat maximální interval dotazování (MaxPollIntervalInSecondsvýchozí nastavení: 5 sekund, limit: 1–300 sekund), který definuje maximální povolený interval dotazování pro připojení Long Polling ve službě Azure SignalR . Pokud další požadavek na hlasování nedorazí do maximálního intervalu dotazování, služba zavře připojení klienta.

Pokyny k přidání služby jako závislosti do produkčního nasazení najdete v tématu Publikování aplikace ASP.NET Core SignalR do služby Aplikace Azure Service.

Další informace naleznete v tématu:

Azure Container Apps

Podrobnější zkoumání škálování aplikací na straně Blazor serveru ve službě Azure Container Apps najdete v tématu Škálování aplikací ASP.NET Core Apps v Azure. Tento kurz vysvětluje, jak vytvořit a integrovat služby potřebné k hostování aplikací v Azure Container Apps. V této části jsou uvedeny také základní kroky.

  1. Podle pokynů v spřažení relací v Azure Container Apps (dokumentace k Azure) nakonfigurujte službu Azure Container Apps pro spřažení relací.

  2. Služba ASP.NET Core Data Protection (DP) musí být nakonfigurovaná tak, aby uchovávala klíče v centralizované lokalitě, ke které mají přístup všechny instance kontejneru. Klíče je možné ukládat ve službě Azure Blob Storage a chránit je pomocí služby Azure Key Vault. Služba DP používá klíče k deserializaci Razor komponent. Pokud chcete nakonfigurovat službu DP tak, aby používala službu Azure Blob Storage a Azure Key Vault, projděte si následující balíčky NuGet:

    Poznámka:

    Pokyny k přidávání balíčků do aplikací .NET najdete v článcích v části Instalace a správa balíčků na webu Pracovní postup používání balíčků (dokumentace k NuGetu). Ověřte správné verze balíčků na NuGet.org.

  3. Aktualizujte Program.cs následujícím zvýrazněným kódem:

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

    Předchozí změny umožňují aplikaci spravovat službu DP pomocí centralizované škálovatelné architektury. DefaultAzureCredential zjistí aplikaci kontejneru spravovanou identity po nasazení kódu do Azure a použije ji k připojení k úložišti objektů blob a trezoru klíčů aplikace.

  4. Pokud chcete vytvořit spravovanou identity aplikaci kontejneru a udělit jí přístup k úložišti objektů blob a trezoru klíčů, proveďte následující kroky:

    1. Na webu Azure Portal přejděte na stránku přehledu aplikace kontejneru.
    2. V levém navigačním panelu vyberte Konektor služby.
    3. V horním navigačním panelu vyberte + Vytvořit .
    4. V rozevírací nabídce Vytvořit připojení zadejte následující hodnoty:
      • Kontejner: Vyberte aplikaci kontejneru, kterou jste vytvořili pro hostování aplikace.
      • Typ služby: Vyberte úložiště objektů blob.
      • Předplatné: Vyberte předplatné, které vlastní aplikaci kontejneru.
      • Název připojení: Zadejte název .scalablerazorstorage
      • Typ klienta: Vyberte .NET a pak vyberte Další.
    5. Vyberte Spravovaný identity systém a vyberte Další.
    6. Použijte výchozí nastavení sítě a vyberte Další.
    7. Jakmile Azure ověří nastavení, vyberte Vytvořit.

    Opakujte předchozí nastavení trezoru klíčů. Na kartě Základy vyberte příslušnou službu trezoru klíčů a klíč.

IIS

Při použití služby IIS povolte:

Další informace najdete v doprovodných materiálech a externích odkazech prostředků IIS v tématu Publikování aplikace ASP.NET Core do služby IIS.

Kubernetes

Vytvořte definici příchozího přenosu dat s následujícími poznámkami Kubernetes pro spřažení relací:

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 na serveru Nginx

Postupujte podle pokynů pro aplikaci ASP.NET Core SignalR s následujícími změnami:

  • location Změňte cestu z /hubroute (location /hubroute { ... }) na kořenovou cestu / (location / { ... }).
  • Odeberte konfiguraci pro ukládání do vyrovnávací paměti proxy serveru (proxy_buffering off;), protože nastavení platí jenom pro události odeslané serverem (SSE), které nejsou relevantní pro Blazor interakce klienta aplikace.

Další informace a pokyny ke konfiguraci najdete v následujících zdrojích informací:

Linux na serveru Apache

Pokud chcete hostovat aplikaci za Apache v Linuxu Blazor , nakonfigurujte ProxyPass provoz HTTP a WebSockets.

V následujícím příkladu:

  • Kestrel server běží na hostitelském počítači.
  • Aplikace naslouchá provozu na portu 5000.
ProxyPreserveHost   On
ProxyPassMatch      ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass           /_blazor ws://localhost:5000/_blazor
ProxyPass           / http://localhost:5000/
ProxyPassReverse    / http://localhost:5000/

Povolte následující moduly:

a2enmod   proxy
a2enmod   proxy_wstunnel

Zkontrolujte chyby webSocket v konzole prohlížeče. Ukázkové chyby:

  • Firefox nemůže navázat připojení k serveru na adrese ws://the-domain-name.tld/_blazor?id=XXX
  • Chyba: Přenos WebSockets se nepovedlo spustit: Chyba: Při přenosu došlo k chybě.
  • Chyba: Spuštění přenosu LongPolling: TypeError: this.transport není definován
  • Chyba: Nelze se připojit k serveru s žádným z dostupných přenosů. WebSockety selhaly
  • Chyba: Nelze odeslat data, pokud připojení není ve stavu Připojeno.

Další informace a pokyny ke konfiguraci najdete v následujících zdrojích informací:

Měření latence sítě

JS K měření latence sítě je možné použít interoperabilitu, jak ukazuje následující příklad.

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

Pro přiměřené uživatelské rozhraní doporučujeme trvalou latenci uživatelského rozhraní 250 ms nebo méně.

Správa paměti

Na serveru se vytvoří nový okruh pro každou uživatelskou relaci. Každá uživatelská relace odpovídá vykreslení jednoho dokumentu v prohlížeči. Například více karet vytváří více relací.

Blazor udržuje konstantní připojení k prohlížeči, kterému se říká okruh, který relaci inicioval. Připojení se můžou kdykoliv ztratit z několika důvodů, například když uživatel ztratí síťové připojení nebo náhle zavře prohlížeč. Když dojde ke ztrátě připojení, má mechanismus obnovení, Blazor který umístí omezený počet okruhů do "odpojeného" fondu, což klientům poskytuje omezenou dobu pro opětovné připojení a opětovné navázání relace (výchozí hodnota: 3 minuty).

Blazor Potom okruh uvolní a relaci zahodí. Od tohoto okamžiku je okruh způsobilý pro uvolňování paměti (GC) a je nárokován při aktivaci kolekce pro generování uvolňování paměti okruhu. Jedním z důležitých aspektů, které je potřeba pochopit, je, že okruhy mají dlouhou životnost, což znamená, že většina objektů kořenových okruhem nakonec dosáhne Gen 2. V důsledku toho se tyto objekty nemusí zobrazit, dokud nedojde k kolekci Gen2.

Obecné měření využití paměti

Požadavky:

  • Aplikace musí být publikovaná v konfiguraci vydané verze . Měření konfigurace ladění nejsou relevantní, protože vygenerovaný kód neodpovídá kódu použitému pro produkční nasazení.
  • Aplikace musí běžet bez připojeného ladicího programu, protože to může také ovlivnit chování aplikace a zkazit výsledky. V sadě Visual Studio spusťte aplikaci bez ladění tak>, že v řádku nabídek vyberete možnost Spustit ladění bez ladění nebo pomocí klávesnice Ctrl+F5.
  • Zvažte různé typy paměti, abyste pochopili, kolik paměti skutečně používá .NET. Obecně platí, že vývojáři kontrolují využití paměti aplikace ve Správci úloh v operačním systému Windows, což obvykle nabízí horní mez skutečné paměti, která se používá. Další informace najdete v následujících článcích:

Využití paměti použité na Blazor

Paměť, kterou blazor používáme, vypočítáme následujícím způsobem:

(Aktivní okruhy × paměti pro jednotlivé okruhy) + (odpojené okruhy × paměti na okruh)

Množství paměti, které okruh používá, a maximální potenciální aktivní okruhy, které může aplikace udržovat, je do značné míry závislé na způsobu zápisu aplikace. Maximální počet možných aktivních okruhů je přibližně popsán takto:

Maximální dostupná paměť / pro paměť = na okruh Maximální potenciální aktivní okruhy

Aby došlo Blazork nevrácené paměti, musí být splněny následující podmínky:

  • Paměť musí být přidělena architekturou, nikoli aplikací. Pokud v aplikaci přidělíte pole o velikosti 1 GB, musí aplikace spravovat vyřazení pole.
  • Paměť nesmí být aktivně používána, což znamená, že okruh není aktivní a byl vyřazen z mezipaměti odpojených okruhů. Pokud máte spuštěné maximální aktivní okruhy, dochází nedostatek paměti, jedná se o problém se škálováním, nevracení paměti.
  • Spustila se uvolňování paměti (GC) pro generování uvolňování paměti okruhu, ale systém uvolňování paměti nemůže tento okruh deklarovat, protože jiný objekt v architektuře má silný odkaz na okruh.

V jiných případech nedochází k nevrácení paměti. Pokud je okruh aktivní (připojený nebo odpojený), okruh se stále používá.

Pokud se kolekce pro generování uvolňování paměti okruhu nespustí, paměť se nevyvolá, protože uvolňování paměti v daném okamžiku nemusí uvolnit paměť.

Pokud se kolekce pro generování uvolňování paměti spustí a uvolní okruh, musíte ověřit paměť na statistikách uvolňování paměti, nikoli v procesu, protože .NET se může rozhodnout zachovat virtuální paměť aktivní.

Pokud paměť není uvolněná, musíte najít okruh, který není aktivní nebo odpojený a který je rootovaný jiným objektem v rozhraní. V každém jiném případě je nemožnost uvolnit paměť problémem aplikace v kódu vývojáře.

Snížení využití paměti

Pokud chcete snížit využití paměti aplikace, použijte některou z následujících strategií:

  • Omezte celkové množství paměti používané procesem .NET. Další informace naleznete v tématu Možnosti konfigurace modulu runtime pro uvolňování paměti.
  • Snižte počet odpojených okruhů.
  • Zkraťte dobu, po které může být okruh v odpojeném stavu.
  • Ruční spuštění uvolňování paměti pro provedení kolekce během období výpadků.
  • Nakonfigurujte uvolňování paměti v režimu pracovní stanice, který agresivně aktivuje uvolňování paměti místo režimu serveru.

Velikost haldy pro některé prohlížeče mobilních zařízení

Při vytváření Blazor aplikace, která běží na klientovi a cílí na prohlížeče mobilních zařízení, zejména Safari v iOSu, může být vyžadováno snížení maximální paměti aplikace s vlastností EmccMaximumHeapSize MSBuild. Další informace najdete v tématu Hostitel a nasazení ASP.NET Core Blazor WebAssembly.

Další akce a důležité informace

  • Zachyťte výpis paměti procesu, když jsou požadavky na paměť vysoké, a identifikujte objekty, které využívají nejvíce paměti a kde jsou tyto objekty rootovány (co obsahuje odkaz na ně).
  • Můžete prozkoumat statistiky o tom, jak se paměť ve vaší aplikaci chová pomocí dotnet-counters. Další informace naleznete v tématu Zkoumání čítačů výkonu (dotnet-counters).
  • I když se aktivuje GC, technologie .NET se místo okamžitého vrácení do operačního systému zachytá do paměti, protože je pravděpodobné, že znovu použije paměť v blízké budoucnosti. Tím se zabrání neustálému potvrzení a rušení paměti, což je nákladné. To se projeví, pokud použijete dotnet-counters , protože uvidíte, že k GCS dochází a množství využité paměti klesne na 0 (nula), ale neuvidíte snížení čítače pracovní sady, což je znaménko, že .NET drží paměť, aby ji znovu použila. Další informace o nastavení souboru projektu (.csproj) pro řízení tohoto chování naleznete v tématu Možnosti konfigurace modulu runtime pro uvolňování paměti.
  • Uvolňování paměti serveru neaktivuje, dokud nezodpovědí, že je naprosto nezbytné, aby se zabránilo zmrazení vaší aplikace a domnívá se, že aplikace je jediná věc spuštěná na počítači, takže může používat veškerou paměť v systému. Pokud má systém 50 GB, systém se před aktivací kolekce Gen2 pokusí použít plnou 50 GB dostupné paměti.
  • Informace o konfiguraci uchovávání odpojených okruhů najdete v pokynech k ASP.NET CoreBlazorSignalR.

Měření paměti

  • Publikujte aplikaci v konfiguraci vydané verze.
  • Spusťte publikovanou verzi aplikace.
  • Nepřipojujte ladicí program ke spuštěné aplikaci.
  • Aktivuje se vynucená komprimace kolekce Gen2 (GC.Collect(2, GCCollectionMode.Aggressive | GCCollectionMode.Forced, blocking: true, compacting: true)) uvolní paměť?
  • Zvažte, jestli vaše aplikace přiděluje objekty na haldě velkého objektu.
  • Testujete růst paměti poté, co se aplikace zahřeje požadavky a zpracování? Obvykle existují mezipaměti, které se naplní při prvním spuštění kódu, které přidají konstantní množství paměti do stopy aplikace.