Události
Mistrovství Světa v Power BI DataViz
14. 2. 16 - 31. 3. 16
Se 4 šance na vstup, můžete vyhrát konferenční balíček a udělat to na LIVE Grand Finale v Las Vegas
Další informaceTento prohlížeč se už nepodporuje.
Upgradujte na Microsoft Edge, abyste mohli využívat nejnovější funkce, aktualizace zabezpečení a technickou podporu.
Poznámka
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Upozornění
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
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 v tomto článku ve verzi .NET 9.
Autor: Mike Rousos
Tento článek obsahuje pokyny pro maximalizaci výkonu a spolehlivosti aplikací ASP.NET Core.
Ukládání do mezipaměti je popsáno v několika částech tohoto článku. Další informace najdete v tématu Přehled ukládání do mezipaměti v ASP.NET Core.
V tomto článku je cesta horkého kódu definována jako cesta kódu, která se často volá a kde dochází k velké části doby provádění. Horké cesty kódu obvykle omezují škálování aplikace na více instancí a výkon a jsou popsány v několika částech tohoto článku.
ASP.NET aplikace Core by měly být navrženy tak, aby zpracovávaly mnoho požadavků současně. Asynchronní rozhraní API umožňují malému fondu vláken zpracovávat tisíce souběžných požadavků tím, že nečeká na blokující volání. Místo čekání na dokončení dlouhotrvající synchronní úlohy může vlákno fungovat na jiném požadavku.
Běžným problémem s výkonem v aplikacích ASP.NET Core je blokování volání, která by mohla být asynchronní. Mnoho synchronních blokujících volání vede k hladovění fondu vláken a snížení doby odezvy.
Neblokujte asynchronní provádění voláním Task.Wait nebo Task<TResult>.Result.
Nezískajte zámky v běžných cestách kódu. ASP.NET aplikace Core fungují nejlépe, když jsou navrženy tak, aby spouštěly kód paralelně.
NevolejteTask.Run a okamžitě ho nečekejte. ASP.NET Core už spouští kód aplikace na normálních vláknech fondu vláken, takže volání Task.Run
vede pouze k nadbytečné plánování fondu vláken. I když by naplánovaný kód blokoval vlákno, Task.Run
nezabrání tomu.
Profiler, například PerfView, lze použít k vyhledání vláken často přidávaných do fondu vláken. Událost Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start
označuje vlákno přidané do fondu vláken.
Webová stránka by neměla načítat velké objemy dat najednou. Při vracení kolekce objektů zvažte, jestli by to mohlo vést k problémům s výkonem. Určete, jestli by návrh mohl vést k následujícím špatným výsledkům:
Přidáním stránkování zmírníte předchozí scénáře. Při použití parametrů velikosti stránky a indexu stránky by vývojáři měli upřednostnění návrhu vrácení částečného výsledku. Pokud je požadován úplný výsledek, stránkování by se mělo použít k asynchronnímu naplnění dávek výsledků, aby nedocházelo k zamykání prostředků serveru.
Další informace o stránkování a omezení počtu vrácených záznamů najdete tady:
Vrácení IEnumerable<T>
z akce vede k synchronní iteraci kolekce serializátorem. Výsledkem je blokování volání a potenciál hladovění fondu vláken. Pokud se chcete vyhnout synchronnímu výčtu, použijte ToListAsync
před vrácením výčtu.
Počínaje ASP.NET Core 3.0 IAsyncEnumerable<T>
lze použít jako alternativu k IEnumerable<T>
výčtu asynchronně. Další informace naleznete v tématu Návratové typy akcí kontroleru.
Uvolňování paměti .NET Core spravuje přidělení a uvolnění paměti automaticky v aplikacích ASP.NET Core. Automatické uvolňování paměti obecně znamená, že se vývojáři nemusí starat o to, jak nebo kdy je paměť uvolněna. Čištění neodkazovaných objektů však trvá čas procesoru, takže vývojáři by měli minimalizovat přidělování objektů v horkých cestách kódu. Uvolňování paměti je zvláště nákladné u velkých objektů (>= 85 000 bajtů). Velké objekty jsou uloženy na velké haldě objektů a vyžadují úplné uvolňování paměti (generace 2) k vyčištění. Na rozdíl od 0. generace a 1. generace kolekce vyžaduje dočasné pozastavení spouštění aplikací. Časté přidělování a rušení přidělování velkých objektů může způsobit nekonzistentní výkon.
Doporučení:
Problémy s pamětí, například předchozí, je možné diagnostikovat kontrolou statistik uvolňování paměti (GC) v nástroji PerfView a zkoumáním:
Další informace naleznete v tématu Uvolňování paměti a výkon.
Interakce s úložištěm dat a dalšími vzdálenými službami jsou často nejpomalejšími částmi aplikace ASP.NET Core. Efektivní čtení a zápis dat je důležité pro dobrý výkon.
Doporučení:
.Where
příkazy , .Select
nebo .Sum
příkazy), aby filtrování prováděla databáze.Následující přístupy můžou zlepšit výkon ve vysoce škálovatelných aplikacích:
Před potvrzením základu kódu doporučujeme měřit dopad předchozích přístupů s vysokým výkonem. Další složitost kompilovaných dotazů nemusí ospravedlnit zlepšení výkonu.
Problémy s dotazy je možné zjistit kontrolou času stráveného přístupem k datům pomocí Application Insights nebo pomocí nástrojů pro profilaci. Většina databází také zpřístupní statistiky týkající se často spouštěných dotazů.
I když HttpClient implementuje IDisposable
rozhraní, je navržené pro opakované použití. Zavřené HttpClient
instance ponechá sokety otevřené ve TIME_WAIT
stavu po krátkou dobu. Pokud se často používá cesta kódu, která vytváří a odstraňuje HttpClient
objekty, aplikace může vyčerpat dostupné sokety.
HttpClientFactory
byl představen v ASP.NET Core 2.1 jako řešení tohoto problému. Zpracovává sdružování připojení HTTP za účelem optimalizace výkonu a spolehlivosti. Další informace najdete v tématu Použití HttpClientFactory
k implementaci odolných požadavků HTTP.
Doporučení:
HttpClient
instance přímo.HttpClient
. Další informace najdete v tématu Použití HttpClientFactory k implementaci odolných požadavků HTTP.Chcete, aby byl veškerý kód rychlý. Nejčastějšími cestami kódu, které se nazývají, jsou pro optimalizaci nejdůležitější. Tady jsou některé z nich:
Doporučení:
Většinu požadavků na aplikaci ASP.NET Core může zpracovat kontroler nebo model stránky, který volá nezbytné služby a vrací odpověď HTTP. U některých požadavků, které zahrnují dlouhotrvající úlohy, je lepší, aby celý proces odezvy požadavku byl asynchronní.
Doporučení:
ASP.NET základní aplikace se složitými front-endy často obsluhuje mnoho souborů JavaScriptu, CSS nebo obrázků. Výkon počátečních požadavků na načtení je možné vylepšit pomocí:
Doporučení:
environment
ke zpracování prostředí Development
i Production
prostředí.Zmenšení velikosti odpovědi obvykle zvyšuje rychlost odezvy aplikace, často výrazně. Jedním ze způsobů, jak snížit velikost datové části, je komprimovat odpovědi aplikace. Další informace naleznete v tématu Komprese odpovědí.
Každá nová verze ASP.NET Core zahrnuje vylepšení výkonu. Optimalizace v .NET Core a ASP.NET Core znamenají, že novější verze obecně mají vyšší výkon než starší verze. Například .NET Core 2.1 přidala podporu zkompilovaných regulárních výrazů a využila výhod span<T>. ASP.NET Core 2.2 přidala podporu pro HTTP/2. ASP.NET Core 3.0 přidává mnoho vylepšení , která snižují využití paměti a zlepšují propustnost. Pokud je výkon prioritou, zvažte upgrade na aktuální verzi ASP.NET Core.
Výjimky by měly být vzácné. Vyvolání a zachycení výjimek je pomalé vzhledem k jiným vzorům toku kódu. Z tohoto důvodu by se výjimky neměly používat k řízení normálního toku programu.
Doporučení:
Diagnostické nástroje aplikací, jako je Application Insights, můžou pomoct identifikovat běžné výjimky v aplikaci, které můžou ovlivnit výkon.
Všechny vstupně-výstupní operace v ASP.NET Core jsou asynchronní. Servery implementují Stream
rozhraní, které má synchronní i asynchronní přetížení. Asynchronní vlákna by se měla upřednostňovat, aby nedocházelo k blokování vláken ve fondu vláken. Blokování vláken může vést k hladovění fondu vláken.
Nedělejte to: Následující příklad používá ReadToEnd. Blokuje aktuální vlákno a čeká na výsledek. Toto je příklad synchronizace přes asynchronní synchronizaci.
public class BadStreamReaderController : Controller
{
[HttpGet("/contoso")]
public ActionResult<ContosoData> Get()
{
var json = new StreamReader(Request.Body).ReadToEnd();
return JsonSerializer.Deserialize<ContosoData>(json);
}
}
V předchozím kódu Get
synchronně čte celé tělo požadavku HTTP do paměti. Pokud se klient pomalu nahrává, aplikace se synchronizuje přes async. Aplikace se synchronizuje přes asynchronní, protože Kestrelnepodporuje synchronní čtení.
Udělejte to: Následující příklad používá ReadToEndAsync a neblokuje vlákno při čtení.
public class GoodStreamReaderController : Controller
{
[HttpGet("/contoso")]
public async Task<ActionResult<ContosoData>> Get()
{
var json = await new StreamReader(Request.Body).ReadToEndAsync();
return JsonSerializer.Deserialize<ContosoData>(json);
}
}
Předchozí kód asynchronně přečte celé tělo požadavku HTTP do paměti.
Upozornění
Pokud je požadavek velký, čtení celého textu požadavku HTTP do paměti může vést k podmínce OOM (nedostatek paměti). Objekt OOM může vést k odepření služby. Další informace naleznete v tématu Vyhněte se čtení velkých těla požadavků nebo těla odpovědí do paměti v tomto článku.
Udělejte to: Následující příklad je plně asynchronní pomocí textu požadavku, který není ve vyrovnávací paměti:
public class GoodStreamReaderController : Controller
{
[HttpGet("/contoso")]
public async Task<ActionResult<ContosoData>> Get()
{
return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
}
}
Předchozí kód asynchronně de-serializuje tělo požadavku do objektu C#.
Používejte HttpContext.Request.ReadFormAsync
místo HttpContext.Request.Form
.
HttpContext.Request.Form
může být bezpečně čteno pouze s následujícími podmínkami:
ReadFormAsync
aHttpContext.Request.Form
Nedělejte to: Následující příklad používá HttpContext.Request.Form
.
HttpContext.Request.Form
používá synchronizaci přes asynchronní a může vést k hladovění fondu vláken.
public class BadReadController : Controller
{
[HttpPost("/form-body")]
public IActionResult Post()
{
var form = HttpContext.Request.Form;
Process(form["id"], form["name"]);
return Accepted();
}
Udělejte to: Následující příklad používá HttpContext.Request.ReadFormAsync
k asynchronnímu čtení těla formuláře.
public class GoodReadController : Controller
{
[HttpPost("/form-body")]
public async Task<IActionResult> Post()
{
var form = await HttpContext.Request.ReadFormAsync();
Process(form["id"], form["name"]);
return Accepted();
}
V .NET skončí každé přidělení objektů větší nebo rovno 85 000 bajtů v haldě velkého objektu (LOH). Velké objekty jsou nákladné dvěma způsoby:
Tento blogový příspěvek popisuje problém stručně:
Když je přidělen velký objekt, označí se jako objekt Gen2. Ne Gen 0 jako pro malé objekty. Důsledky jsou, že pokud v LOH dojde nedostatek paměti, GC vyčistí celou spravovanou haldu, nejen LOH. Proto vyčistí gen 0, Gen 1 a Gen2 včetně LOH. Tomu se říká úplné uvolňování paměti a je to časově nejnáročnější uvolňování paměti. U mnoha aplikací může být přijatelné. Ale rozhodně ne pro vysoce výkonné webové servery, kde je potřeba několik vyrovnávacích pamětí pro zpracování průměrného webového požadavku (čtení ze soketu, dekomprimace, dekódování JSON a další).
Uložení velkého textu požadavku nebo odpovědi do jednoho byte[]
nebo string
:
Při použití serializátoru/de-serializátoru, který podporuje pouze synchronní čtení a zápisy (například Json.NET):
Upozornění
Pokud je požadavek velký, může to vést k nedostatku paměti (OOM). Objekt OOM může vést k odepření služby. Další informace naleznete v tématu Vyhněte se čtení velkých těla požadavků nebo těla odpovědí do paměti v tomto článku.
ASP.NET Core 3.0 používá System.Text.Json ve výchozím nastavení serializaci JSON. System.Text.Json:
Newtonsoft.Json
.IHttpContextAccessor.HttpContext vrátí HttpContext
aktivní požadavek při přístupu z vlákna požadavku. Hodnota by nemělaIHttpContextAccessor.HttpContext
být uložena v poli nebo proměnné.
Nedělejte to: Následující příklad uloží pole HttpContext
do pole a pokusí se ho použít později.
public class MyBadType
{
private readonly HttpContext _context;
public MyBadType(IHttpContextAccessor accessor)
{
_context = accessor.HttpContext;
}
public void CheckAdmin()
{
if (!_context.User.IsInRole("admin"))
{
throw new UnauthorizedAccessException("The current user isn't an admin");
}
}
}
Předchozí kód často zachycuje hodnotu null nebo nesprávnou hodnotu HttpContext
v konstruktoru.
Udělejte to: Následující příklad:
HttpContext
Používá pole ve správný čas a kontroluje null
.public class MyGoodType
{
private readonly IHttpContextAccessor _accessor;
public MyGoodType(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public void CheckAdmin()
{
var context = _accessor.HttpContext;
if (context != null && !context.User.IsInRole("admin"))
{
throw new UnauthorizedAccessException("The current user isn't an admin");
}
}
}
HttpContext
není bezpečný pro přístup z více vláken.
HttpContext
Přístup z více vláken paralelně může vést k neočekávanému chování, jako je například zastavení reakce serveru, chybové ukončení a poškození dat.
Nedělejte to: Následující příklad vytvoří tři paralelní požadavky a zaprokoluje cestu příchozího požadavku před a za odchozím požadavkem HTTP. Cesta požadavku je přístupná z více vláken, potenciálně paralelně.
public class AsyncBadSearchController : Controller
{
[HttpGet("/search")]
public async Task<SearchResults> Get(string query)
{
var query1 = SearchAsync(SearchEngine.Google, query);
var query2 = SearchAsync(SearchEngine.Bing, query);
var query3 = SearchAsync(SearchEngine.DuckDuckGo, query);
await Task.WhenAll(query1, query2, query3);
var results1 = await query1;
var results2 = await query2;
var results3 = await query3;
return SearchResults.Combine(results1, results2, results3);
}
private async Task<SearchResults> SearchAsync(SearchEngine engine, string query)
{
var searchResults = _searchService.Empty();
try
{
_logger.LogInformation("Starting search query from {path}.",
HttpContext.Request.Path);
searchResults = _searchService.Search(engine, query);
_logger.LogInformation("Finishing search query from {path}.",
HttpContext.Request.Path);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed query from {path}",
HttpContext.Request.Path);
}
return await searchResults;
}
Udělejte to: Následující příklad zkopíruje všechna data z příchozího požadavku před provedením tří paralelních požadavků.
public class AsyncGoodSearchController : Controller
{
[HttpGet("/search")]
public async Task<SearchResults> Get(string query)
{
string path = HttpContext.Request.Path;
var query1 = SearchAsync(SearchEngine.Google, query,
path);
var query2 = SearchAsync(SearchEngine.Bing, query, path);
var query3 = SearchAsync(SearchEngine.DuckDuckGo, query, path);
await Task.WhenAll(query1, query2, query3);
var results1 = await query1;
var results2 = await query2;
var results3 = await query3;
return SearchResults.Combine(results1, results2, results3);
}
private async Task<SearchResults> SearchAsync(SearchEngine engine, string query,
string path)
{
var searchResults = _searchService.Empty();
try
{
_logger.LogInformation("Starting search query from {path}.",
path);
searchResults = await _searchService.SearchAsync(engine, query);
_logger.LogInformation("Finishing search query from {path}.", path);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed query from {path}", path);
}
return await searchResults;
}
HttpContext
platí pouze za předpokladu, že v kanálu ASP.NET Core existuje aktivní požadavek HTTP. Celý kanál ASP.NET Core je asynchronní řetězec delegátů, který spouští všechny požadavky. Po dokončení vráceného Task
z tohoto řetězce se HttpContext
recykluje.
Nedělejte to: Následující příklad používá, async void
aby se požadavek HTTP dokončil při prvním await
dosažení:
async void
je vždy špatným postupem v aplikacích ASP.NET Core.HttpResponse
dokončení požadavku HTTP.public class AsyncBadVoidController : Controller
{
[HttpGet("/async")]
public async void Get()
{
await Task.Delay(1000);
// The following line will crash the process because of writing after the
// response has completed on a background thread. Notice async void Get()
await Response.WriteAsync("Hello World");
}
}
Udělejte to: Následující příklad vrátí Task
rozhraní, takže požadavek HTTP se nedokončí, dokud se akce nedokončí.
public class AsyncGoodTaskController : Controller
{
[HttpGet("/async")]
public async Task Get()
{
await Task.Delay(1000);
await Response.WriteAsync("Hello World");
}
}
Nedělejte to: Následující příklad ukazuje, že uzavření zachycuje HttpContext
z Controller
vlastnosti. Toto je špatný postup, protože pracovní položka by mohla:
HttpContext
.[HttpGet("/fire-and-forget-1")]
public IActionResult BadFireAndForget()
{
_ = Task.Run(async () =>
{
await Task.Delay(1000);
var path = HttpContext.Request.Path;
Log(path);
});
return Accepted();
}
Udělejte to: Následující příklad:
[HttpGet("/fire-and-forget-3")]
public IActionResult GoodFireAndForget()
{
string path = HttpContext.Request.Path;
_ = Task.Run(async () =>
{
await Task.Delay(1000);
Log(path);
});
return Accepted();
}
Úlohy na pozadí by se měly implementovat jako hostované služby. Další informace najdete v tématu Úlohy na pozadí s hostovanými službami.
Nedělejte to: Následující příklad ukazuje uzavření, které zachycuje DbContext
z parametru Controller
akce. To je špatná praxe. Pracovní položka se může spustit mimo obor požadavku. Rozsah ContosoDbContext
se vztahuje na požadavek, což vede k .ObjectDisposedException
[HttpGet("/fire-and-forget-1")]
public IActionResult FireAndForget1([FromServices]ContosoDbContext context)
{
_ = Task.Run(async () =>
{
await Task.Delay(1000);
context.Contoso.Add(new Contoso());
await context.SaveChangesAsync();
});
return Accepted();
}
Udělejte to: Následující příklad:
IServiceScopeFactory
je jedenton.ContosoDbContext
příchozí požadavek.[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory
serviceScopeFactory)
{
_ = Task.Run(async () =>
{
await Task.Delay(1000);
await using (var scope = serviceScopeFactory.CreateAsyncScope())
{
var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();
context.Contoso.Add(new Contoso());
await context.SaveChangesAsync();
}
});
return Accepted();
}
Následující zvýrazněný kód:
ContosoDbContext
se ze správného oboru.[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory
serviceScopeFactory)
{
_ = Task.Run(async () =>
{
await Task.Delay(1000);
await using (var scope = serviceScopeFactory.CreateAsyncScope())
{
var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();
context.Contoso.Add(new Contoso());
await context.SaveChangesAsync();
}
});
return Accepted();
}
ASP.NET Core neukládá tělo odpovědi HTTP do vyrovnávací paměti. Při prvním zápisu odpovědi:
Nedělejte to: Následující kód se pokusí přidat hlavičky odpovědi po spuštění odpovědi:
app.Use(async (context, next) =>
{
await next();
context.Response.Headers["test"] = "test value";
});
V předchozím kódu vyvolá výjimku, context.Response.Headers["test"] = "test value";
pokud next()
byla zapsána do odpovědi.
Udělejte to: Následující příklad zkontroluje, jestli se před úpravou hlaviček spustila odpověď HTTP.
app.Use(async (context, next) =>
{
await next();
if (!context.Response.HasStarted)
{
context.Response.Headers["test"] = "test value";
}
});
Udělejte to: Následující příklad používá HttpResponse.OnStarting
k nastavení hlaviček před vyprázdněním hlaviček odpovědi do klienta.
Kontrola, jestli odpověď nezačala, umožňuje registraci zpětného volání, která se vyvolá těsně před zápisem hlaviček odpovědi. Kontrola, jestli odpověď nezačala:
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
context.Response.Headers["someheader"] = "somevalue";
return Task.CompletedTask;
});
await next();
});
Komponenty očekávají, že se budou volat pouze v případě, že je možné zpracovat odpověď a manipulovat s ní.
Při vnitroprocesovém hostování aplikace ASP.NET Core běží ve stejném procesu jako příslušný pracovní proces služby IIS. Hostování v procesu poskytuje lepší výkon při hostování mimo proces, protože požadavky se nepřesunou přes adaptér zpětné smyčky. Adaptér zpětné smyčky je síťové rozhraní, které vrací odchozí síťový provoz zpět do stejného počítače. Služba IIS zajišťuje správu procesů s využitím Aktivační služby procesů systému Windows (WAS).
Projekty ve výchozím nastavení využívají model hostování v procesu v ASP.NET Core 3.0 a novějším.
Další informace najdete v tématu Hostitel ASP.NET Core ve Windows se službou IIS.
HttpRequest.ContentLength
je null, pokud hlavička Content-Length
není přijata. Hodnota Null v tomto případě znamená, že délka textu požadavku není známa; neznamená to, že délka je nula. Vzhledem k tomu, že všechna porovnání s hodnotou null (s výjimkou ==
) vrací hodnotu false, může se například vrátitRequest.ContentLength > 1024
, false
když je velikost textu požadavku větší než 1024. Nevíme, že to může vést k bezpečnostním otvorům v aplikacích. Možná si myslíte, že chráníte před příliš velkými požadavky, když nejste.
Další informace najdete v této odpovědi StackOverflow.
Pokyny k vytvoření spolehlivé, zabezpečené, výkonné, testovatelné a škálovatelné aplikace ASP.NET Core najdete v vzorech podnikových webových aplikací. K dispozici je kompletní ukázková webová aplikace pro produkční kvalitu, která implementuje vzory.
Zpětná vazba k produktu ASP.NET Core
ASP.NET Core je open source projekt. Vyberte odkaz pro poskytnutí zpětné vazby:
Události
Mistrovství Světa v Power BI DataViz
14. 2. 16 - 31. 3. 16
Se 4 šance na vstup, můžete vyhrát konferenční balíček a udělat to na LIVE Grand Finale v Las Vegas
Další informace