RE:NODE

Hosting aplikací11 min čtení

Blazor Server vs WebAssembly: kolik stojí hosting

Čím se po nasazení liší okruhy Blazor Server a Blazor WebAssembly: paměť na uživatele, SignalR, latence, velikost stahování, prerendering a režimy Blazor Web App.

1 čtenář

Blazor Server spouští tvoje komponenty na serveru a změny rozhraní posílá do prohlížeče přes spojení SignalR: každá otevřená záložka je živý okruh (circuit), který drží stav v paměti serveru, každé kliknutí je round trip a paměť serveru roste s počtem souběžných uživatelů, ne s počtem požadavků. Blazor WebAssembly stáhne do prohlížeče runtime .NET i tvoji aplikaci a spouští ji tam: server jen servíruje statické soubory a API, ale první návštěva stojí stahování o několika megabajtech a nic na klientovi nemůže být tajné. Od .NET 8 už nemusíš vybírat jedno pro celou aplikaci - model Blazor Web App ve výchozím stavu vykresluje stránky staticky na serveru a nechává každou komponentu zvolit interaktivitu Server, WebAssembly nebo Auto.

Pro hosting je otázka jednoduchá: kolik lidí bude připojeno současně, jak daleko jsou od serveru a může si server dovolit pamatovat si je všechny? Tento článek dává na každou z těch otázek čísla.

Režimy vykreslování v jedné tabulce#

Blazor Web App (výchozí šablona od .NET 8) podporuje čtyři způsoby vykreslení komponenty:

Režim vykreslováníKde běží kódInteraktivníNáklad serveru na návštěvníka
Statický na straně serveru (SSR)Server, na každý požadavekNe (jen formuláře a odkazy)Běžný požadavek HTTP
InteractiveServerServer, v okruhuAnoŽivý okruh po celou návštěvu
InteractiveWebAssemblyProhlížečAnoStatické soubory, pak volání API
InteractiveAutoNejdřív server, pak prohlížečAnoOkruh, dokud se runtime neuloží do cache

Statické SSR se snadno přehlédne a často je správná odpověď. Stránka, která zobrazuje data a odesílá formulář, žádnou interaktivitu nepotřebuje; vykreslí se jako Razor Page a po odeslání odpovědi serveru nestojí nic. Vylepšená navigace (enhanced navigation) způsobí, že odkazy mezi statickými stránkami působí jako single-page aplikace, a [StreamRendering] dovolí pomalé stránce poslat nejdřív rozvržení a data až po jejich příchodu.

Interaktivita se nastavuje po komponentách nebo pro celou aplikaci:

Program.cs
builder.Services.AddRazorComponents()    .AddInteractiveServerComponents()    .AddInteractiveWebAssemblyComponents();app.MapRazorComponents<App>()    .AddInteractiveServerRenderMode()    .AddInteractiveWebAssemblyRenderMode()    .AddAdditionalAssemblies(typeof(Client._Imports).Assembly);

Komponenta pak deklaruje @rendermode InteractiveServer (nebo jiný z režimů) a interaktivitu platí jen ona a její potomci. Starší samostatné šablony Blazor Server a „ASP.NET Core hosted“ WebAssembly jsou od .NET 8 pryč; existující aplikace na nich postavené dál běží.

Jak funguje Blazor Server#

Když se načte stránka s interaktivní serverovou komponentou, prohlížeč otevře zpět na server spojení SignalR, přednostně přes WebSockets. Server vytvoří okruh: instanci každé interaktivní komponenty na stránce, jejich stav, strom vykreslení a scoped kontejner dependency injection. Odtud dál:

  1. Uživatel klikne na tlačítko. Prohlížeč pošle událost přes spojení.
  2. Server spustí obslužnou funkci události, znovu vykreslí dotčené komponenty a spočítá rozdíl oproti poslednímu vykreslení.
  3. Server pošle rozdíl zpět a prohlížeč upraví DOM.
SignalRobslužné funkce událostíZáložka prohlížečeblazor.web.jsReverzní proxyupgrade na WebSocketOkruhstav komponentDatabázedotazy
Jedno kliknutí v aplikaci Blazor Server

Proto se Blazor Server programuje jako desktopová aplikace: kód může volat databázi přímo, nemusíš stavět vrstvu API a do prohlížeče se nikdy nedostane nic citlivého. Proto jsou také jeho náklady jiné než u běžné webové aplikace. Okruh žije tak dlouho, dokud je záložka otevřená, ať uživatel něco dělá, nebo ne, a scoped služby v něm - včetně DbContext z EF Core, pokud ho vložíš přímo - žijí stejně dlouho.

Paměť a CPU na uživatele#

Vlastní doporučení Microsoftu uvádí minimum zhruba 250 KB paměti serveru na připojeného uživatele u velmi jednoduché aplikace. Skutečné aplikace drží mnohem víc: pole každé komponenty, data, která jsi do nich načetl, mřížky s tisíci řádky a scoped služby. Okruh s mřížkou o 5 000 řádcích a jejími daty se měří v megabajtech a drží je po celou návštěvu.

Hrubý způsob odhadu: změř jeden okruh. Otevři aplikaci v jedné záložce, poznamenej si graf paměti, otevři dvacet dalších záložek na nejtěžší stránce a nárůst vyděl dvaceti. Pak to vynásob počtem lidí, o kterých čekáš, že budou mít aplikaci otevřenou ve stejný okamžik - ne za den, ale najednou.

Souběžní uživateléLehké stránky (asi 0,5 MB každá)Těžké stránky (asi 5 MB každá)
2010 MB100 MB
200100 MB1 GB
1,000500 MB5 GB

Hodnoty na uživatele jsou ilustrativní, ne naměřené na tvé aplikaci; jde o tvar. Aplikace Blazor Server, která je v pořádku pro kancelář plnou uživatelů, může být špatná pro veřejný web, kam může najednou dorazit tisíce lidí. Základní paměť procesu navrch k okruhům popisuje paměť .NET a garbage collection.

Nastavení okruhů, která to omezují:

Nastavení CircuitOptionsVýchozíCo dělá
DisconnectedCircuitRetentionPeriod3 minutesJak dlouho se odpojený okruh drží pro opětovné připojení
DisconnectedCircuitMaxRetained100Kolik odpojených okruhů se drží najednou
JSInteropDefaultCallTimeout1 minuteTimeout pro volání do JavaScriptu
MaxBufferedUnacknowledgedRenderBatches10Dávky vykreslení ve frontě pro pomalého klienta
DetailedErrorsfalsePosílat podrobnosti o výjimkách do prohlížeče - jen pro vývoj

Data drž mimo komponenty, kdykoli to jde: načti stránku výsledků, ne celou tabulku. Pro dlouhé seznamy použij Virtualize, aby se vykreslovaly jen viditelné řádky. DbContext vytvářej pro každou operaci přes IDbContextFactory<T> místo vkládání jednoho na celý život okruhu.

CPU je obvykle menší problém, protože většina událostí je levná. Záleží na něm, když mnoho uživatelů spustí najednou drahé vykreslení - dashboard překreslovaný na časovači pro každého připojeného uživatele je zátěž, která roste s publikem, i když se nikdo ničeho nedotkne.

Latence a spojení#

Každá interakce v Blazor Server čeká na síť. Při 20 ms mezi uživatelem a serverem působí tlačítko okamžitě. Při 150 ms je psaní do pole, které se překresluje při každém stisku klávesy, vazké a drag-and-drop působí rozbitě. Pokud jsou tvoji uživatelé na jiném kontinentu než server, otestuj to odtud dřív, než se zavážeš k režimu Server. Co čísla znamenají v praxi, vysvětluje latence, jitter a ztráta paketů.

Spojení také musí přežít. Blazor Server potřebuje trvalé spojení přes každou proxy mezi prohlížečem a Kestrelem:

  • WebSockets musí projít upgradem. Pokud nemohou, SignalR přejde na long polling, který funguje, ale zpomaluje každou událost a stojí víc požadavků. Záložka sítě v prohlížeči ukazuje odpověď 101 Switching Protocols, když WebSockets fungují.
  • Idle timeouty na proxy musí být delší než keep-alive SignalR, který se ve výchozím stavu ozývá každých 15 sekund.
  • Aplikace musí vidět skutečného klienta a schéma přes předávané hlavičky - viz ASP.NET Core za reverzní proxy.

Když spojení spadne, prohlížeč zobrazí překryv s opětovným připojováním a zkouší znovu. Pokud se připojí v rámci doby uchování, okruh pokračuje tam, kde byl. Pokud ne, stav je pryč a uživatel musí stránku znovu načíst. Mobilní uživatelé přepínající sítě a notebooky probouzející se ze spánku na to narážejí neustále a .NET 9 i 10 zlepšily zážitek z opětovného připojení; .NET 10 přidává způsob, jak uložit stav okruhu, aby uživatel mohl pokračovat i po delší pauze. Než se na to spolehneš, ověř, co podporuje tvoje verze.

Jak funguje Blazor WebAssembly#

Režim WebAssembly pošle tvoji aplikaci do prohlížeče. První návštěva stáhne runtime .NET zkompilovaný do WebAssembly, knihovny frameworku, které aplikace používá, a tvoje vlastní assembly, a pak všechno běží lokálně. Poté prohlížeč soubory uloží do cache a další návštěvy startují rychle.

Kompromisy zrcadlí ty u režimu Server:

  • Stahování. Publikovaná aplikace je zredukovaná (trimming je u WebAssembly zapnutý ve výchozím stavu) a komprimovaná, ale počítej s první dávkou dat měřenou v megabajtech, ne v kilobajtech - malá aplikace skončí zhruba na 2 až 4 MB komprimovaně, větší na víc. Na pomalém mobilním připojení to znamená vteřiny na načítací obrazovce.
  • Žádná tajemství. Všechno na klientovi si může uživatel přečíst. Connection stringy, klíče API a obchodní pravidla, která nesmí jít obejít, patří na server, za API.
  • Rychlost provádění. Ve výchozím stavu je tvůj kód interpretován runtimem WebAssembly. Kompilace předem (ahead-of-time, <RunAOTCompilation>true</RunAOTCompilation>, která k buildu potřebuje workload wasm-tools) výrazně zrychlí kód náročný na CPU a výrazně zvětší stahování.
  • Náklady serveru. Pro rozhraní téměř žádné. Server jednou na uživatele doručí statické soubory a pak odpovídá na volání API, která se škálují jako jakékoli jiné API.

Hostování aplikace WebAssembly#

Samostatná aplikace Blazor WebAssembly (šablona blazorwasm) se publikuje do složky statických souborů pod wwwroot a může ji servírovat jakýkoli statický hosting. O tom, zda to bude fungovat, rozhodují tři detaily:

  1. Fallback routing. Blazor zpracovává trasy v prohlížeči, takže požadavek na /orders/42 musí vrátit index.html, ne 404. Statický hosting potřebuje pravidlo přepisu pro neznámé cesty; jak ho přidat, závisí na serveru. Běžné případy pokrývá hosting statických webů.
  2. Komprese a typy MIME. Publikování zapisuje předkomprimované kopie .br a .gz každého souboru. Servírovat je a servírovat .wasm jako application/wasm je rozdíl mezi prvním načtením o 3 MB a o 10 MB.
  3. Hlavičky cache. Soubory runtime jsou opatřeny otiskem, takže se mohou ukládat do cache dlouho; index.html ne, jinak uživatelé po nasazení dál načítají staré verze. Úvahu najdeš v vysvětlení hlaviček HTTP cache.

API, které klient volá, je samostatná aplikace ASP.NET Core. Pokud je na jiném originu než statické soubory, nastav na API CORS. V Blazor Web App s komponentami InteractiveWebAssembly servíruje server ASP.NET Core soubory klienta i API z jednoho originu, což se CORS úplně vyhne a je to nejjednodušší uspořádání k hostování.

Prerendering a dvojí načtení#

Interaktivní komponenty v Blazor Web App se ve výchozím stavu prerenderují: server vykreslí HTML jednou, aby se stránka objevila okamžitě a vyhledávače ji přečetly, a pak komponenta nastartuje znovu v interaktivním režimu a vykreslí se podruhé. Jakékoli načítání dat v OnInitializedAsync proto proběhne dvakrát - jednou při prerenderingu, podruhé, když převezme okruh nebo runtime WebAssembly.

Dva způsoby, jak to řešit:

  • Zachovej stav mezi dvěma vykresleními. PersistentComponentState (od .NET 8) serializuje data z prerenderu do stránky a předá je interaktivnímu vykreslení. .NET 10 přidává deklarativní atribut [PersistentState] na vlastnosti komponenty, který dělá totéž s méně kódu.
  • Vypni prerendering u komponent, kde na prvním vykreslení nezáleží, pomocí @rendermode @(new InteractiveServerRenderMode(prerender: false)). Stránka pak tu komponentu nezobrazí, dokud nezačne interaktivita.

Prerendering také znamená, že kód v OnInitializedAsync běží na serveru i pro komponentu WebAssembly, takže nesmí předpokládat, že je v prohlížeči - žádná volání IJSRuntime tam.

Nasazení, restarty a ztracené okruhy#

Nasazení restartuje proces a každý okruh Blazor Server s ním zanikne. Uživatelé vidí překryv s opětovným připojováním, připojení selže, protože server, který znali, je pryč, a musí stránku načíst znovu, přičemž přijdou o všechno neuložené. Se zapnutým nasazením při pushi tohle dělá každý push do větve každému připojenému uživateli.

Takže u aplikace Blazor Server: ukládej uživatelský vstup často, nasazuj v klidných hodinách a nepovažuj okruh za úložiště. Aplikace WebAssembly jsou mnohem odpustivější - rozhraní běží v prohlížeči dál, zatímco se API restartuje, a selžou jen volání v té mezeře. Čemu se s jednou instancí vyhnout jde a čemu ne, popisuje nasazení bez výpadku na malém serveru.

Ještě jedna věc musí restart přežít: klíče Data Protection pro antiforgery tokeny a ověřovací cookies. Pokud se vygenerují znovu, každý uživatel je odhlášen. Kam je uložit, ukazuje nasazení aplikace ASP.NET Core z GitHubu.

Jak vybrat#

  • Převážně obsah a formuláře: statické SSR, s interaktivitou jen u komponent, které ji potřebují. Zdaleka nejlevnější na hosting.
  • Interní nástroje a admin panely s desítkami uživatelů blízko serveru: InteractiveServer. Žádné API k postavení, rychlé první načtení, paměť v tomto měřítku není problém.
  • Veřejné aplikace s mnoha souběžnými uživateli nebo uživateli daleko od serveru: InteractiveWebAssembly s API. Server se škáluje s požadavky, ne s otevřenými záložkami.
  • Chceš rychlé první načtení a pak WebAssembly: InteractiveAuto, s tím, že teď píšeš komponenty, které musí fungovat na obou místech.

Praktický příklad to rozdělení zkonkrétní. Rezervační web sportovního klubu má veřejný rozvrh, rezervační formulář a obrazovku pro personál na správu kurtů. Rozvrh je statické SSR se stream renderingem: čte se mnohem víc než cokoli jiného a udržet ho otevřený nestojí nic. Rezervační formulář je také statické SSR - běžné odeslání formuláře s validací okruh nepotřebuje. Obrazovka pro personál, kterou používají čtyři lidé v síti klubu, je InteractiveServer: bohatá, okamžitá, mluví přímo s databází. Výsledek pohodlně běží na malém tarifu, protože jediné okruhy, které existují, patří čtyřem lidem, kterým prospívají. Stejný web postavený celý v režimu Server by držel okruh pro každého člena, který se v sobotu ráno dívá na rozvrh.

FAQ#

Kolik uživatelů zvládne aplikace Blazor Server na 2 GB?

Záleží úplně na tom, co každý okruh drží. Lehká aplikace s půl megabajtem na uživatele pojme vedle samotného procesu několik set souběžných uživatelů; datově náročná jich zvládne pár desítek. Změř jeden okruh a vynásob.

Potřebuje Blazor WebAssembly vůbec server s .NET?

Ne pro rozhraní. Samostatná aplikace WebAssembly jsou statické soubory a servíruje je jakýkoli statický hosting. Server s .NET potřebuješ jen pro API, které volá - a to budeš mít téměř vždy.

Proč se moje aplikace Blazor Server odpojuje každou minutu?

Idle timeout proxy je kratší než interval keep-alive, nebo WebSockets neprocházejí, takže záložní transport pořád vyprší. Podívej se v záložce sítě, jaký transport se používá.

Dá se Blazor Server škálovat na více serverů?

Ano, se sticky sessions, aby spojení každého uživatele vždy dorazilo na server, který drží jeho okruh. Na jednom serveru ta otázka nevzniká; škálování obyčejného SignalR pokrývá aplikace v reálném čase se SignalR.

Je InteractiveAuto to nejlepší z obou světů?

Je to dobrá výchozí volba pro veřejné aplikace, ale každá komponenta Auto musí běžet na serveru i v prohlížeči, takže nemůže sahat přímo do databáze. Ta podmínka je cena za rychlé první načtení.


Komentáře

Zcela anonymně: žádný účet, žádný e-mail, žádné cookie. Ukládáme jen jméno, které napíšeš, text a čas - nic dalšího. Počet odkazů je omezený a značkování se nevykresluje.

0/2000