Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Note
Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.
Warning
A ASP.NET Core ezen verziója már nem támogatott. További információt a .NET és a .NET Core támogatási szabályzatában talál. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .
Készítette : Mike Rousos
Ez a cikk útmutatást nyújt a ASP.NET Core-alkalmazások teljesítményének és megbízhatóságának maximalizálásához.
Gyorsítótárazás agresszíven
A gyorsítótárazásról a cikk több részében tárgyalunk. További információ: A ASP.NET Core gyorsítótárazásának áttekintése.
A gyakori kód elérési útjainak ismertetése
Ebben a cikkben a gyakori kódot tartalmazó elérési út olyan kódútvonalként van definiálva, amelyet gyakran hívnak meg, és ahol a végrehajtási idő nagy része történik. Az intenzív használatú kódútvonalak általában korlátozzák az alkalmazások növelését és teljesítményét, ezt több részben tárgyaljuk a cikkben.
A hívások blokkolásának elkerülése
ASP.NET Core-alkalmazásokat úgy kell megtervezni, hogy egyszerre több kérést dolgozzanak fel. Az aszinkron API-k lehetővé teszik, hogy egy kis szálkészlet több ezer egyidejű kérést kezeljen azáltal, hogy nem várakozik a blokkoló hívásokra. Ahelyett, hogy egy hosszú ideig futó szinkron feladatra várna, a szál egy másik kéréssel is foglalkozhat.
Az ASP.NET Core-alkalmazások gyakori teljesítményproblémája az aszinkron hívások blokkolása. Számos szinkron blokkoló hívás a szálkészlet éhezéséhez és a válaszidő romlásához vezet.
Ne tiltsa le az aszinkron végrehajtást hívással Task.Wait vagy Task<TResult>.Result.
Ne szerezzen zárolásokat a szokásos kódutakban. ASP.NET Core-alkalmazások akkor működnek a legjobban, ha a kódot párhuzamosan futtatják.
Ne hívjon, Task.Run és azonnal várja meg. ASP.NET Core már futtatja az alkalmazás kódját normál szálkészlet szálakon, így a Task.Run hívása csak felesleges, szükségtelen ütemezést eredményez a szálkészletben. Még ha az ütemezett kód blokkolna is egy szálat, Task.Run ez nem akadályozza meg.
- Végezze el a gyakori kódot aszinkron módon.
- Az adathozzáférést, az I/O-t és a hosszan futó műveleti API-kat aszinkron módon hívhatja meg, ha elérhető aszinkron API.
- Ne használjon Task.Run szinkron API-t aszinkronná tételére.
- Végezze el a vezérlő- ésRazor oldalműveleteket aszinkron módon. A teljes hívásverem aszinkron az async/await minták kihasználása érdekében.
- Fontolja meg az olyan üzenetközvetítők használatát, mint az Azure Service Bus, a megszakítás nélküli hívások átterhelésére.
A profilkészítő, például a PerfView, segítségével megtalálhatja a Thread Pool-hoz gyakran hozzáadott szálakat. Az Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start esemény a szálkészlethez hozzáadott szálat jelzi.
Nagyobb gyűjtemények visszaadása több kisebb oldalon
A weblapok egyszerre nem tölthetnek be nagy mennyiségű adatot. Objektumgyűjtemény visszaadásakor fontolja meg, hogy teljesítményproblémákhoz vezethet-e. Állapítsa meg, hogy a terv a következő rossz eredményeket hozhatja-e:
- OutOfMemoryException vagy magas memóriahasználat
- Szálkészlet éhezése (lásd az alábbi megjegyzéseket: IAsyncEnumerable<T>)
- Lassú válaszidők
- Gyakori szemétgyűjtés
Adjon hozzá lapozást az előző forgatókönyvek enyhítése érdekében. Az oldalméret és oldalindex paraméterek használatával a fejlesztőknek előnyben kell részesíteniük a részleges eredmény visszaadását. Ha teljes eredményre van szükség, a kiszolgálói erőforrások zárolásának elkerülése érdekében lapozást kell használni az eredmények kötegeinek aszinkron feltöltéséhez.
További információ a lapozásról és a visszaadott rekordok számának korlátozásáról:
Visszatérés IEnumerable<T> vagy IAsyncEnumerable<T>
A műveletből való IEnumerable<T> visszatérés szinkronizált gyűjteményiterációt eredményez a szerializáló által. Ennek eredménye a hívások blokkolása és a szálkészlet éhezésének lehetősége. A szinkron számbavétel elkerülése érdekében használja ToListAsync a számbavétel visszaadása előtt.
Az ASP.NET Core 3.0-val kezdődően a IAsyncEnumerable<T> az aszinkron enumeráció alternatívájaként IEnumerable<T> is használható. További információ: Vezérlőművelet visszatérési típusai.
Nagy méretű objektumfoglalások minimalizálása
A .NET szemétgyűjtő automatikusan kezeli a memória lefoglalását és felszabadítását ASP.NET Core-alkalmazásokban. Az automatikus szemétgyűjtés általában azt jelenti, hogy a fejlesztőknek nem kell aggódniuk a memória felszabadításának módja vagy időzítése miatt. A nem hivatkozott objektumok megtisztítása azonban processzoridőt igényel, ezért a fejlesztőknek minimálisra kell csökkenteni az objektumok gyakori kódot tartalmazó elérési utakon való elosztását. A szemétgyűjtés különösen költséges nagy objektumok esetén (>= 85 000 bájt). A nagyméretű objektumok a nagy méretű objektum halomán vannak tárolva, és teljes (2. generációs) szemétgyűjtést igényelnek a tisztításhoz. A 0. és az 1. generációs gyűjteményektől eltérően a 2. generációs gyűjteményekhez az alkalmazás végrehajtásának ideiglenes felfüggesztése szükséges. A nagy objektumok gyakori lefoglalása és lefoglalásának megszüntetése inkonzisztens teljesítményt okozhat.
Recommendations:
- Fontolja meg a gyakran használt nagy objektumok gyorsítótárazását. A nagy objektumok gyorsítótárazása megakadályozza, hogy költséges lefoglalásokat kelljen végezni.
- Nagyméretű tömbök tárolására használjon ArrayPool<T> készletpuffereket.
- Ne foglal le sok, rövid élettartamú nagy objektumot a gyakori kódot tartalmazó útvonalakon.
Az előzőhez hasonló memóriaproblémák diagnosztizálhatók a PerfView szemétgyűjtési (GC) statisztikáinak áttekintésével és a következők vizsgálatával:
- A szemétgyűjtés szüneteltetési ideje.
- A processzoridő hány százaléka kerül szemétgyűjtésbe.
- Hány szemétgyűjtési művelet van a 0., 1. és 2. generációban?
További információ: Szemétgyűjtés és teljesítmény.
Adathozzáférés és I/O optimalizálása
Az adattárral és más távoli szolgáltatásokkal folytatott interakciók gyakran a ASP.NET Core-alkalmazások leglassabb részei. Az adatok hatékony olvasása és írása kritikus fontosságú a jó teljesítmény érdekében.
Recommendations:
- Az összes adatelérési API-t aszinkron módon kell meghívni.
- Ne kérjen le több adatot, mint amennyi szükséges. A lekérdezések írásával csak az aktuális HTTP-kéréshez szükséges adatokat adja vissza.
- Fontolja meg az adatbázisból vagy távoli szolgáltatásból lekért gyakran használt adatok gyorsítótárazását, ha a kissé elavult adatok elfogadhatók. A forgatókönyvtől függően használja a MemoryCache-t vagy az DistributedCache-t. További információért lásd: Válasz gyorsítótárazása az ASP.NET Core-ban.
- Minimalizálja a hálózati körutakat. A cél az, hogy több hívás helyett egyetlen hívásban kérje le a szükséges adatokat.
- Használjonnyomkövetési nélküli lekérdezéseket az Entity Framework Core-ban, amikor adatokhoz csak olvasásra van szükség. EF Core hatékonyabban tudja visszaadni a nyomkövetés nélküli lekérdezések eredményeit.
-
Végezze el a LINQ-lekérdezések szűrését
.Whereés összesítését (például ,.Selectvagy.Sumutasításokkal), hogy a szűrést az adatbázis végezze el. - Fontolja meg, hogy az EF Core bizonyos lekérdezési operátorokat ügyféloldalon old fel, ami nem hatékony lekérdezés-végrehajtáshoz vezethet. További információ: Ügyfélértékelési teljesítményproblémák.
- Ne használjon leképezési lekérdezéseket gyűjteményeken, ami "N + 1" SQL-lekérdezések futtatását eredményezheti. További információ: Korrelált al lekérdezések optimalizálása.
A következő megközelítések növelhetik a teljesítményt a nagy léptékű alkalmazásokban:
Javasoljuk, hogy a kódbázis véglegesítése előtt mérje fel az előző nagy teljesítményű megközelítések hatását. Előfordulhat, hogy a lefordított lekérdezések további összetettsége nem indokolja a teljesítmény javulását.
A lekérdezési problémák az Application Insights vagy profilkészítési eszközök használatával történő adathozzáféréssel töltött idő áttekintésével észlelhetők. A legtöbb adatbázis a gyakran végrehajtott lekérdezésekkel kapcsolatos statisztikákat is elérhetővé teszi.
HTTP-kapcsolatok készlete a HttpClientFactory használatával
Bár HttpClient implementálja az IDisposable interfészt, újra felhasználható. A bezárt HttpClient példányok rövid ideig nyitva hagyják az TIME_WAIT állapotban lévő aljzatokat. Ha gyakran használ objektumokat létrehozó és megsemmisítő HttpClient kódútvonalat, az alkalmazás kimerítheti a rendelkezésre álló szoftvercsatornákat.
HttpClientFactory ASP.NET Core 2.1-ben került bevezetésre a probléma megoldásaként. Kezeli a HTTP-kapcsolatok készletezését a teljesítmény és a megbízhatóság optimalizálása érdekében. További információ: Rugalmas HttpClientFactory HTTP-kérések implementálása.
Recommendations:
-
Ne hozzon létre és semmisítse meg közvetlenül a
HttpClientpéldányokat. - Használja a HttpClientFactory-t példányok lekéréséhez . További információ: Rugalmas HTTP-kérések implementálása a HttpClientFactory használatával.
A gyakori kódútvonalak gyors megőrzése
Azt szeretné, hogy az összes kód gyors legyen. Az optimalizálás szempontjából a leggyakrabban használt kódútvonalak a legkritikusabbak. Ezek a következők:
- Az alkalmazás kérésfeldolgozási folyamatának köztes szoftver összetevői, különösen a köztes szoftverek a folyamat korai szakaszában futnak. Ezek az összetevők nagy hatással vannak a teljesítményre.
- Minden kéréshez vagy kérésenként többször végrehajtott kód. Ilyenek például az egyéni naplózás, az engedélyezési kezelők vagy az átmeneti szolgáltatások inicializálása.
Recommendations:
- Ne használjon egyéni köztes szoftverösszetevőket hosszú ideig futó feladatokkal.
- Használjon teljesítményprofilozási eszközöket, például a Visual Studio diagnosztikai eszközeit vagy a PerfView-t a gyakori kódot tartalmazó elérési utak azonosításához.
Hosszan futó feladatok végrehajtása HTTP-kérelmeken kívül
A ASP.NET Core-alkalmazásra irányuló kérelmek többségét egy vezérlő vagy oldalmodell kezelheti, amely meghívja a szükséges szolgáltatásokat, és HTTP-választ ad vissza. Bizonyos, hosszú ideig futó feladatokat tartalmazó kérések esetében jobb, ha a teljes kérés-válasz folyamatot aszinkron módon hajtja végre.
Recommendations:
- Ne várja meg, amíg a hosszú ideig futó feladatok befejeződnek a szokásos HTTP-kérésfeldolgozás részeként.
- Fontolja meg a hosszú ideig futó kérések kezelését háttérfolyamatokkal, vagy esetleg egy Azure Function-nel és/vagy üzenetközvetítő, például az Azure Service Bus használatával. A folyamaton kívüli munka elvégzése különösen hasznos a processzorigényes feladatok esetében.
- Használjon valós idejű kommunikációs lehetőségeket, például SignalRaz ügyfelekkel való aszinkron kommunikációhoz.
Ügyfél eszközök kicsinyítése
ASP.NET Összetett előtérrendszerekkel rendelkező Core-alkalmazások gyakran számos JavaScript-, CSS- vagy képfájlt szolgálnak ki. A kezdeti terheléskérések teljesítménye a következőkkel javítható:
- Bundling, amely több fájlt egyesít egybe.
- Minifying, amely csökkenti a fájlok méretét azáltal, hogy eltávolítja a térközt és a megjegyzéseket.
Recommendations:
-
Használja a csomagkézbesítési és minifikációs irányelveket, amelyek a kompatibilis eszközöket említik, és bemutatják, hogyan használható ASP.NET Core címkéje
environmentmind aDevelopmentkörnyezetek, mindProductiona környezetek kezelésére. - Fontolja meg más külső eszközök, például a Webpack használatát az összetett ügyféleszköz-kezeléshez.
Válaszok tömörítése
A válasz méretének csökkentése általában növeli az alkalmazások válaszkészségét, gyakran drámai módon. A hasznos adatok méretének csökkentésének egyik módja az alkalmazás válaszainak tömörítése. További információ: Választömörítés.
A legújabb ASP.NET Core-kiadás használata
A ASP.NET Core minden új kiadása teljesítménybeli fejlesztéseket tartalmaz. A .NET és a ASP.NET Core optimalizálása azt jelenti, hogy az újabb verziók általában felülmúlják a régebbi verziókat. A .NET Core 2.1 például támogatja a lefordított reguláris kifejezéseket, és kihasználta a Span<T> előnyeit. ASP.NET Core 2.2 támogatja a HTTP/2-t. ASP.NET Core 3.0 számos olyan fejlesztést tartalmaz, amelyek csökkentik a memóriahasználatot és javítják az átviteli sebességet. Ha a teljesítmény prioritást élvez, érdemes lehet a ASP.NET Core aktuális verziójára frissíteni.
Kivételek minimalizálása
A kivételeknek ritkanak kell lenniük. A kivételek kivetése és elfogása lassú a többi kódfolyamat-mintához képest. Emiatt nem szabad kivételeket használni a normál programfolyamat szabályozásához.
Recommendations:
- Ne használja a kivételek dobását vagy elkapását a normál programfolyamat részeként, különösen kritikus kódútvonalakon.
- Használja az alkalmazás logikáját a kivételt okozó feltételek észlelésére és kezelésére.
- Dobja ki vagy fogja el a kivételeket szokatlan vagy váratlan körülmények esetén.
Az alkalmazásdiagnosztikai eszközök, például az Application Insights segíthetnek azonosítani a teljesítményt befolyásoló gyakori kivételeket az alkalmazásokban.
Kerülje a szinkron olvasást vagy írást a HttpRequest/HttpResponse törzsben
A ASP.NET Core-ban minden I/O aszinkron. A kiszolgálók implementálják az Stream interfészt, amely szinkron és aszinkron túlterheléssel is rendelkezik. Az aszinkron elemeket előnyben kell részesíteni a szálkészlet szálainak blokkolásának elkerülése érdekében. A szálak blokkolása a szálkészlet éhezéséhez vezethet.
Ne csinálja ezt: Az alábbi példa a ReadToEnd-t használja. Az aktuális szálat letiltja, hogy megvárja az eredményt. Ez egy példa a aszinkron-fölött-szinkron működésre.
public class BadStreamReaderController : Controller
{
[HttpGet("/contoso")]
public ActionResult<ContosoData> Get()
{
var json = new StreamReader(Request.Body).ReadToEnd();
return JsonSerializer.Deserialize<ContosoData>(json);
}
}
Az előző kódban Get szinkron módon beolvassa a teljes HTTP-kérelem törzsét a memóriába. Ha az ügyfél lassan tölt fel, az alkalmazás szinkronizálást végez aszinkron módon. Az alkalmazás aszinkron módon szinkronizál, mert Kestrelnem támogatja a szinkron olvasást.
Tegye a következőt: Az alábbi példa olvasás közben használja ReadToEndAsync és nem blokkolja a szálat.
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);
}
}
Az előző kód aszinkron módon beolvassa a teljes HTTP-kérelem törzsét a memóriába.
Warning
Ha a kérés nagy, a teljes HTTP-kérelem törzsének a memóriába való beolvasása memóriahiányhoz (OOM) vezethet. Az OOM szolgáltatásmegtagadást eredményezhet. További információért lásd az ebben a cikkben található Nagyméretű kérelemtörzsek vagy választestek memóriába olvasásának elkerülése részt.
Tegye a következőt: Az alábbi példa teljesen aszinkron módon működik, nem pufferelt kérelemtörzset használva:
public class GoodStreamReaderController : Controller
{
[HttpGet("/contoso")]
public async Task<ActionResult<ContosoData>> Get()
{
return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
}
}
Az előző kód aszinkron módon de-szerializálja a kérelem törzsét egy C# objektummá.
A ReadFormAsync előnyben részesítése a Request.Form-hez
HttpContext.Request.ReadFormAsynchelyett használja HttpContext.Request.Form.
HttpContext.Request.Form csak a következő feltételekkel olvasható biztonságosan:
- Az űrlap beolvasását egy
ReadFormAsynchívása végezte el. - A gyorsítótárban tárolt űrlapértéket a következő használatával olvassuk be:
HttpContext.Request.Form
Ne tegye a következőt: Az alábbi példa a következőt használja HttpContext.Request.Form:
HttpContext.Request.Forma szinkron az aszinkron felett használatával a szálkészlet éhezéséhez vezethet.
public class BadReadController : Controller
{
[HttpPost("/form-body")]
public IActionResult Post()
{
var form = HttpContext.Request.Form;
Process(form["id"], form["name"]);
return Accepted();
}
Tegye a következőt: Az alábbi példa az űrlap törzsének aszinkron olvasására használja HttpContext.Request.ReadFormAsync .
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();
}
Kerülje a nagyméretű kérelemtestek vagy választestek memóriába való beolvasását
A .NET-ben minden 85 000 bájtnál nagyobb vagy egyenlő objektumfoglalás a nagy objektum halomba (LOH) kerül. A nagy objektumok kétféleképpen drágák:
- A foglalási költség magas, mert egy újonnan foglalású nagy objektum memóriáját fel kell szabadítani. A CLR garantálja, hogy az összes újonnan lefoglalt objektum memóriája törlődik.
- A LOH-t a halom többi részével gyűjtjük össze. A LOH teljes szemétgyűjtést vagy Gen2-gyűjteményt igényel.
Ez a blogbejegyzés tömören ismerteti a problémát:
Egy nagy objektum lefoglalásakor Gen 2 objektumként jelölik meg. Nem Gen 0, mint a kisebb objektumok esetében. A következmény az, hogy ha elfogy a memória a LOH-ban, a GC nem csak a LOH-ot, hanem az egész felügyelt halmot megtisztítja. Így megtisztítja Gen 0, Gen 1 és Gen 2 beleértve LOH. Ezt nevezik teljes szemétgyűjtésnek, és ez a leginkább időigényes szemétgyűjtés. Számos alkalmazás esetében elfogadható. De határozottan nem a nagy teljesítményű webkiszolgálók esetében, ahol kevés nagy memóriapufferre van szükség egy átlagos webes kérés kezeléséhez (olvasás szoftvercsatornából, dekompresszió, JSON dekódolása stb.).
Nagy kérés- vagy választörzs tárolása egyetlen byte[] vagy string:
- Előfordulhat, hogy a LOH-ban gyorsan elfogy a hely.
- Teljesítményproblémákat okozhat az alkalmazás számára, mert teljes memóriagyűjtések futnak.
Szinkron adatfeldolgozási API használata
Amikor egy olyan szerializálót/deszerializálót használ, amely csak a szinkron olvasásokat és írásokat támogatja (például Json.NET):
- Aszinkron módon tárolja az adatokat a memóriában, mielőtt átadná őket a szerializálónak/deszerializálónak.
Warning
Ha a kérés nagy, az memóriahiányhoz (OOM) vezethet. Az OOM szolgáltatásmegtagadást eredményezhet. További információért lásd az ebben a cikkben található Nagyméretű kérelemtörzsek vagy választestek memóriába olvasásának elkerülése részt.
ASP.NET Core 3.0 alapértelmezés szerint JSON-szerializálást használ System.Text.Json . System.Text.Json:
- JSON-t olvas és ír aszinkron módon.
- UTF-8 szövegre van optimalizálva.
- Általában nagyobb teljesítményt nyújt, mint
Newtonsoft.Json.
Ne tárolja az IHttpContextAccessor.HttpContext mezőt
Az IHttpContextAccessor.HttpContext visszaadja az HttpContext aktív kérést, amikor a kérelemszálból éri el. A IHttpContextAccessor.HttpContext mezőt nem szabad egy mezőben vagy változóban tárolni.
Ne tegye a következőt: Az alábbi példa egy mezőben tárolja a HttpContext mezőt, majd később megpróbálja használni.
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");
}
}
}
Az előző kód gyakran rögzít null értéket vagy helytelent HttpContext a konstruktorban.
Tegye a következőt: A következő példa:
- IHttpContextAccessor-t egy mezőben tárolja.
- A mezőt a
HttpContextmegfelelő időpontban használja, és ellenőrzi anullmezőt.
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");
}
}
}
Ne érje el a HttpContextet több szálból
HttpContext
nincs szálbiztos.
HttpContext A több szálból való párhuzamos hozzáférés váratlan viselkedést eredményezhet, például a kiszolgáló leállítja a válaszadást, összeomlik és adatsérülést okoz.
Ne tegye a következőt: Az alábbi példa három párhuzamos kérést készít, és naplózza a bejövő kérelem elérési útját a kimenő HTTP-kérés előtt és után. A kérelem elérési útja több szálból érhető el, akár párhuzamosan is.
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;
}
Tegye a következőt: Az alábbi példa az összes adatot átmásolja a bejövő kérésből a három párhuzamos kérés végrehajtása előtt.
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;
}
Ne használja a HttpContextet a kérés befejezése után
HttpContext csak akkor érvényes, ha aktív HTTP-kérés található a ASP.NET Core-folyamatban. A teljes ASP.NET Core-folyamat a meghatalmazottak aszinkron lánca, amely minden kérést végrehajt. Amikor a Task láncból visszaadott érték befejeződik, az HttpContext újrafeldolgozódik.
Ne tegye a következőt: Az alábbi példa async void a HTTP-kérést az első await elérésekor teszi teljessé:
- A
async voidhasználata MINDIG rossz gyakorlat az ASP.NET Core-alkalmazásokban. - A példakód a HTTP-kérés befejezése után fér hozzá
HttpResponse. - A késői hozzáférés összeomlást okoz a folyamatban.
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");
}
}
Tegye a következőt: Az alábbi példa egy Task értéket ad vissza a keretrendszernek, így a HTTP-kérés nem fejeződik be, amíg a művelet be nem fejeződik.
public class AsyncGoodTaskController : Controller
{
[HttpGet("/async")]
public async Task Get()
{
await Task.Delay(1000);
await Response.WriteAsync("Hello World");
}
}
Ne rögzítse a HttpContextet háttérszálakban
Ne tegye ezt: Az alábbi példa megmutatja, hogy egy lezárás rögzíti a HttpContext értéket a Controller tulajdonságból. Ez rossz gyakorlat, mert a munkaelem a következőt teheti:
- Végezze a kérelem területén kívül.
- Próbálja meg a hibás
HttpContextbeolvasását.
[HttpGet("/fire-and-forget-1")]
public IActionResult BadFireAndForget()
{
_ = Task.Run(async () =>
{
await Task.Delay(1000);
var path = HttpContext.Request.Path;
Log(path);
});
return Accepted();
}
Tegye a következőt: A következő példa:
- Másolja a háttérfeladatban szükséges adatokat a kérés során.
- A vezérlő nem hivatkozik semmire.
[HttpGet("/fire-and-forget-3")]
public IActionResult GoodFireAndForget()
{
string path = HttpContext.Request.Path;
_ = Task.Run(async () =>
{
await Task.Delay(1000);
Log(path);
});
return Accepted();
}
A háttérfeladatokat üzemeltetett szolgáltatásként kell végrehajtani. További információ: Háttérfeladatok üzemeltetett szolgáltatásokkal.
Ne rögzítse a háttérszálak vezérlőibe injektált szolgáltatásokat
Ne tegyük ezt: Az alábbi példa egy closure-t mutat be, amely megragadja a DbContextController műveletparamétert. Ez egy rossz gyakorlat. A munkaelem a kérelem hatókörén kívül is futtatható. A ContosoDbContext a kérelemhez van rendelve, ami ObjectDisposedException-et eredményez.
[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();
}
Tegye a következőt: A következő példa:
- Egy IServiceScopeFactory elemet szúr be annak érdekében, hogy hatókört hozzon létre a háttérmunkaelemben.
IServiceScopeFactoryegy singleton. - Létrehoz egy új függőséginjektálási hatókört a háttérszálban.
- A vezérlő nem hivatkozik semmire.
- Nem rögzíti a
ContosoDbContextbejövő kérést.
[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();
}
A következő kiemelt kód:
- Létrehoz egy hatókört a háttérművelet tartamára, és szolgáltatásokat old fel belőle.
- A megfelelő hatókörből használja
ContosoDbContext.
[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();
}
A válasz törzsének elindítása után ne módosítsa az állapotkódot vagy a fejléceket
ASP.NET Core nem puffereli a HTTP-válasz törzsét. A válasz első megírása:
- A fejléceket a rendszer a törzs adattömbjével együtt elküldi az ügyfélnek.
- A válaszfejlécek már nem módosíthatók.
Ne tegye a következőt: A következő kód megpróbálja hozzáadni a válaszfejléceket, miután a válasz már elindult:
app.Use(async (context, next) =>
{
await next();
context.Response.Headers["test"] = "test value";
});
Az előző kód kivételt fog eredményezni, context.Response.Headers["test"] = "test value"; ha next() a válaszra íródott.
Tegye a következőt: Az alábbi példa ellenőrzi, hogy a HTTP-válasz elindult-e a fejlécek módosítása előtt.
app.Use(async (context, next) =>
{
await next();
if (!context.Response.HasStarted)
{
context.Response.Headers["test"] = "test value";
}
});
Tegye a következőt: Az alábbi példa a fejlécek beállítása céljából használható HttpResponse.OnStarting, mielőtt a válaszfejlécek ki lesznek küldve az ügyfélnek.
Annak ellenőrzése, hogy a válasz nem kezdődött-e el, lehetővé teszi egy visszahívás regisztrálását, amelyet közvetlenül a válaszfejlécek írása előtt hív meg a rendszer. Annak ellenőrzése, hogy a válasz nem indult-e el:
- Lehetővé teszi a fejlécek éppen időbeni hozzáfűzését vagy felülbírálását.
- Nincs szükség a folyamat következő köztes szoftverének ismeretére.
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
context.Response.Headers["someheader"] = "somevalue";
return Task.CompletedTask;
});
await next();
});
Ne hívja meg a next() függvényt, ha már megkezdte az írást a válasz törzsébe.
Csak akkor hívják meg az összetevőket, ha lehetséges számukra, hogy kezeljék és módosítsák a választ.
Folyamatközi üzemeltetés használata az IIS-vel
A folyamaton belüli üzemeltetés használatával egy ASP.NET Core-alkalmazás ugyanabban a folyamatban fut, mint az IIS feldolgozói folyamata. A folyamaton belüli üzemeltetés jobb teljesítményt nyújt a folyamaton kívüli üzemeltetéshez képest, mivel a kérések nem a visszacsatolási adapteren keresztül futnak. A visszacsatolási adapter egy olyan hálózati adapter, amely a kimenő hálózati forgalmat ugyanahhoz a géphez adja vissza. Az IIS a folyamatkezelést a Windows Folyamataktiválási Szolgáltatás (WAS)segítségével kezeli.
A projektek alapértelmezés szerint a ASP.NET Core 3.0-s vagy újabb verziójában a folyamaton belüli üzemeltetési modellre kerülnek.
További információ: Host ASP.NET Core on Windows with IIS
Ne feltételezze, hogy a HttpRequest.ContentLength értéke nem null
HttpRequest.ContentLength null értékű, ha a Content-Length fejléc nem érkezik meg. Ebben az esetben a null érték azt jelenti, hogy a kérelem törzsének hossza nem ismert; ez nem jelenti azt, hogy a hossz nulla. Mivel minden nullával történő összehasonlítás (kivéve ==) hamis értéket ad vissza, például az Request.ContentLength > 1024 összehasonlítás akkor térhet vissza false értékben, ha a kérés törzsének mérete meghaladja az 1024 bájtot. Az ennek az ismeretének hiányában biztonsági rések alakulhatnak ki az alkalmazásokban. Előfordulhat, hogy úgy gondolja, védelmet nyújt a túl nagy kérések ellen, pedig valójában nem így van.
További információkért tekintse meg ezt a StackOverflow-választ.
Vállalati webalkalmazás-minták
A megbízható, biztonságos, teljesíthető, tesztelhető és méretezhető ASP.NET Core-alkalmazás létrehozásával kapcsolatos útmutatásért tekintse meg Vállalati webalkalmazás-mintákcímű témakört. Rendelkezésre áll egy teljesen felhasználásra kész példawebalkalmazás, amely implementálja a mintákat.