Použití asynchronních metod v ASP.NET 4.5
V tomto kurzu se naučíte základy vytváření asynchronní ASP.NET Web Forms aplikace pomocí Visual Studio Express 2012 pro web, což je bezplatná verze sady Microsoft Visual Studio. Můžete také použít Visual Studio 2012. V tomto kurzu jsou zahrnuté následující části.
- Způsob zpracování požadavků fondem vláken
- Volba synchronních nebo asynchronních metod
- Ukázková aplikace
- Synchronní stránka Gizmos
- Vytvoření asynchronní stránky Gizmos
- Provádění více operací paralelně
- Použití tokenu zrušení
- Konfigurace serveru pro volání webové služby s vysokou souběžností nebo vysokou latencí
Kompletní ukázku pro tento kurz najdete na adrese .
https://github.com/RickAndMSFT/Async-ASP.NET/ na webu GitHubu .
webové stránky ASP.NET 4.5 v kombinaci rozhraní .NET 4.5 umožňují registrovat asynchronní metody, které vrací objekt typu Task. Rozhraní .NET Framework 4 zavedlo koncept asynchronního programování označovaný jako úloha a ASP.NET 4.5 podporuje úlohu. Úkoly jsou reprezentovány typem úlohy a souvisejícími typy v oboru názvů System.Threading.Tasks . Rozhraní .NET Framework 4.5 vychází z této asynchronní podpory s klíčovými slovy await a async , díky kterým je práce s objekty Úloh mnohem méně složitá než předchozí asynchronní přístupy. Klíčové slovo await je syntaktická zkratka označující, že část kódu by měla asynchronně čekat na nějakou jinou část kódu. Klíčové slovo async představuje nápovědu, kterou můžete použít k označení metod jako asynchronních metod založených na úlohách. Kombinace funkcí await, async a task objektu výrazně usnadňuje psaní asynchronního kódu v .NET 4.5. Nový model pro asynchronní metody se nazývá Asynchronní vzor založený na úlohách (TAP). Tento kurz předpokládá, že máte určitou znalost asynchronního programování pomocí klíčových slov await a async a oboru názvů Task .
Další informace o použití klíčových slov await a async a oboru názvů Task najdete v následujících referenčních materiálech.
- Dokument white paper: Asynchrony v .NET
- Nejčastější dotazy k Async/Await
- Asynchronní programování v sadě Visual Studio
Způsob zpracování požadavků fondem vláken
Na webovém serveru udržuje rozhraní .NET Framework fond vláken, které se používají ke službě ASP.NET požadavků. Při přijetí požadavku se odešle vlákno z fondu, které tento požadavek zpracuje. Pokud se požadavek zpracovává synchronně, vlákno, které požadavek zpracovává, je během zpracování požadavku zaneprázdněno a toto vlákno nemůže obsazovacím způsobem zpracovat jiný požadavek.
To nemusí být problém, protože fond vláken může být dostatečně velký, aby pojal mnoho zaneprázdněných vláken. Počet vláken ve fondu vláken je však omezený (výchozí maximum pro .NET 4.5 je 5 000). Ve velkých aplikacích s vysokou souběžností dlouhotrvajících požadavků můžou být všechna dostupná vlákna zaneprázdněná. Tato podmínka se označuje jako omezení počtu vláken. Po dosažení této podmínky webový server zatáhl žádosti do fronty. Pokud se fronta požadavků zaplní, webový server odmítne požadavky se stavem HTTP 503 (Server je příliš zaneprázdněn). Fond vláken CLR má omezení týkající se nových injektáží vláken. Pokud je souběžnost nárazová (to znamená, že váš web může náhle získat velký počet požadavků) a všechna dostupná vlákna požadavků jsou zaneprázdněná kvůli volání back-endu s vysokou latencí, může omezená rychlost vkládání vláken způsobovat, že vaše aplikace reaguje velmi špatně. Kromě toho každé nové vlákno přidané do fondu vláken má režii (například 1 MB paměti zásobníku). Webová aplikace využívající synchronní metody pro obsluhu volání s vysokou latencí, kdy fond vláken roste na výchozí maximum .NET 4.5, což je maximálně 5 000 vláken, by spotřebovala přibližně o 5 GB více paměti než aplikace schopná služby stejné požadavky pomocí asynchronních metod a pouze 50 vláken. Při asynchronní práci nepoužíváte vždy vlákno. Pokud například vytvoříte asynchronní požadavek webové služby, ASP.NET nebude používat žádná vlákna mezi voláním asynchronní metody a operátorem await. Použití fondu vláken k obsluhování požadavků s vysokou latencí může vést k velkým nárokům na paměť a nízkému využití hardwaru serveru.
Zpracování asynchronních požadavků
Ve webových aplikacích, které při spuštění vidí velký počet souběžných požadavků nebo mají nárazové zatížení (při náhlém zvýšení souběžnosti), zvýší asynchronní volání webové služby rychlost odezvy vaší aplikace. Zpracování asynchronního požadavku trvá stejně dlouho jako synchronní požadavek. Pokud například požadavek provede volání webové služby, které vyžaduje k dokončení dvě sekundy, trvá žádost dvě sekundy, ať už se provádí synchronně nebo asynchronně. Během asynchronního volání však vlákno není blokováno v odpovídání na jiné požadavky, zatímco čeká na dokončení prvního požadavku. Asynchronní požadavky proto brání zatěžování požadavků a nárůstu fondu vláken, pokud existuje mnoho souběžných požadavků, které vyvolávají dlouhotrvající operace.
Volba synchronních nebo asynchronních metod
Tato část obsahuje pokyny pro použití synchronních nebo asynchronních metod. To jsou jen pokyny; prozkoumejte každou aplikaci zvlášť a zjistěte, jestli asynchronní metody pomáhají s výkonem.
Obecně platí, že synchronní metody používejte pro následující podmínky:
- Operace jsou jednoduché nebo krátkodobé.
- Jednoduchost je důležitější než efektivita.
- Operace jsou primárně operace procesoru, nikoli operace, které zahrnují velkou režii disku nebo sítě. Použití asynchronních metod u operací vázaných na procesor neposkytuje žádné výhody a vede k vyšší režii.
Obecně platí, že asynchronní metody používejte pro následující podmínky:
Voláte služby, které je možné využívat prostřednictvím asynchronních metod, a používáte .NET 4.5 nebo novější.
Operace jsou vázané na síť nebo vstupně-výstupní operace, nikoli na procesor.
Paralelismus je důležitější než jednoduchost kódu.
Chcete poskytnout mechanismus, který uživatelům umožní zrušit dlouhotrvající požadavek.
Když výhoda přepínání vláken převáží náklady na přepnutí kontextu. Obecně platí, že byste měli metodu nastavit jako asynchronní, pokud synchronní metoda blokuje vlákno požadavku ASP.NET a přitom nepracuje. Tím, že se volání změní na asynchronní, vlákno požadavku ASP.NET není blokováno a nepracuje, zatímco čeká na dokončení požadavku webové služby.
Testování ukazuje, že blokující operace jsou kritickým bodem výkonu lokality a že služba IIS dokáže obsluhovat více požadavků pomocí asynchronních metod pro tato blokující volání.
Ukázka ke stažení ukazuje, jak efektivně používat asynchronní metody. Poskytnutá ukázka byla navržena tak, aby poskytovala jednoduchou ukázku asynchronního programování v ASP.NET 4.5. Ukázka není určena jako referenční architektura pro asynchronní programování v ASP.NET. Ukázkový program volá ASP.NET metody webového rozhraní API , které následně volají Task.Delay pro simulaci dlouhotrvajících volání webových služeb. Většina produkčních aplikací neukazuje tak zřejmé výhody použití asynchronních metod.
Jen málo aplikací vyžaduje, aby všechny metody byly asynchronní. Převod několika synchronních metod na asynchronní často poskytuje nejlepší zvýšení efektivity pro požadované množství práce.
Ukázková aplikace
Ukázkovou aplikaci si můžete stáhnout z https://github.com/RickAndMSFT/Async-ASP.NET webu GitHub . Úložiště se skládá ze tří projektů:
- WebAppAsync: Projekt ASP.NET Web Forms, který využívá službu WebAPIpwg webového rozhraní API. Většina kódu pro tento kurz pochází z tohoto projektu.
- WebAPIpgw: Projekt webového rozhraní API ASP.NET MVC 4, který implementuje
Products, Gizmos and Widgets
kontrolery. Poskytuje data pro projekt WebAppAsync a projekt Mvc4Async . - Mvc4Async: Projekt ASP.NET MVC 4, který obsahuje kód použitý v jiném kurzu. Provádí volání webového rozhraní API do služby WebAPIpwg .
Synchronní stránka Gizmos
Následující kód ukazuje synchronní metodu Page_Load
, která se používá k zobrazení seznamu gizmos. (V tomto článku je gizmo fiktivní mechanické zařízení.)
public partial class Gizmos : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var gizmoService = new GizmoService();
GizmoGridView.DataSource = gizmoService.GetGizmos();
GizmoGridView.DataBind();
}
}
Následující kód ukazuje metodu GetGizmos
služby gizmo.
public class GizmoService
{
public async Task<List<Gizmo>> GetGizmosAsync(
// Implementation removed.
public List<Gizmo> GetGizmos()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
webClient.DownloadString(uri)
);
}
}
}
Metoda GizmoService GetGizmos
předá identifikátor URI službě HTTP webového rozhraní API ASP.NET, která vrátí seznam dat gizmos. Projekt WebAPIpgw obsahuje implementaci webového rozhraní API gizmos, widget
a product
kontrolerů.
Následující obrázek znázorňuje stránku gizmos z ukázkového projektu.
Vytvoření asynchronní stránky Gizmos
Ukázka používá nová klíčová slova async a await (k dispozici v .NET 4.5 a sadě Visual Studio 2012), aby kompilátor zodpovídá za údržbu složitých transformací nezbytných pro asynchronní programování. Kompilátor umožňuje psát kód pomocí konstruktorů synchronního řídicího toku jazyka C# a kompilátor automaticky použije transformace potřebné k použití zpětných volání, aby se zabránilo blokování vláken.
ASP.NET asynchronní stránky musí obsahovat direktivu Page s atributem nastaveným Async
na hodnotu true. Následující kód ukazuje direktivu Page s atributem nastaveným Async
na true pro stránku GizmosAsync.aspx .
<%@ Page Async="true" Language="C#" AutoEventWireup="true"
CodeBehind="GizmosAsync.aspx.cs" Inherits="WebAppAsync.GizmosAsync" %>
Následující kód ukazuje synchronní Page_Load
metodu Gizmos
GizmosAsync
a asynchronní stránku. Pokud váš prohlížeč podporuje element HTML 5 <mark>, uvidíte změny ve GizmosAsync
žlutém zvýraznění.
protected void Page_Load(object sender, EventArgs e)
{
var gizmoService = new GizmoService();
GizmoGridView.DataSource = gizmoService.GetGizmos();
GizmoGridView.DataBind();
}
Asynchronní verze:
protected void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(GetGizmosSvcAsync));
}
private async Task GetGizmosSvcAsync()
{
var gizmoService = new GizmoService();
GizmosGridView.DataSource = await gizmoService.GetGizmosAsync();
GizmosGridView.DataBind();
}
Následující změny byly použity, aby GizmosAsync
stránka byla asynchronní.
- Direktiva Page musí mít
Async
atribut nastavený na hodnotu true. - Metoda
RegisterAsyncTask
se používá k registraci asynchronní úlohy obsahující kód, který běží asynchronně. - Nová
GetGizmosSvcAsync
metoda je označena klíčovým slovem async , které kompilátoru říká, aby vygeneroval zpětná volání pro části těla a automaticky vytvořil vrácenou metoduTask
. - K názvu asynchronní metody se připojil řetězec "Async". Připojení "Async" se nevyžaduje, ale jedná se o konvenci při psaní asynchronních metod.
- Návratový typ nové
GetGizmosSvcAsync
metody jeTask
. NávratovýTask
typ představuje probíhající práci a poskytuje volajícím metody popisovač, přes který mají čekat na dokončení asynchronní operace. - Klíčové slovo await bylo použito pro volání webové služby.
- Asynchronní rozhraní API webové služby bylo volána (
GetGizmosAsync
).
Uvnitř těla metody je GetGizmosSvcAsync
volána další asynchronní metoda GetGizmosAsync
. GetGizmosAsync
okamžitě vrátí hodnotu , Task<List<Gizmo>>
která se nakonec dokončí, jakmile budou data k dispozici. Protože nechcete dělat nic jiného, dokud nebudete mít data gizmo, kód čeká na úkol (pomocí klíčového slova await ). Klíčové slovo await můžete použít pouze v metodách anotovaných klíčovým slovem async .
Klíčové slovo await neblokuje vlákno, dokud se úloha nedokončí. Zaregistruje zbytek metody jako zpětné volání úlohy a okamžitě se vrátí. Jakmile se očekávaná úloha nakonec dokončí, vyvolá toto zpětné volání a tím obnoví provádění metody přímo tam, kde skončila. Další informace o použití klíčových slov awaitasync a oboru názvů Task najdete v asynchronních odkazech.
Následující kód ukazuje metody GetGizmos
a GetGizmosAsync
.
public List<Gizmo> GetGizmos()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
webClient.DownloadString(uri)
);
}
}
public async Task<List<Gizmo>> GetGizmosAsync()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
await webClient.DownloadStringTaskAsync(uri)
);
}
}
Asynchronní změny jsou podobné těm, které se provedly u GizmosAsync výše.
- Podpis metody byl anotován klíčovým slovem async , návratový typ byl změněn na
Task<List<Gizmo>>
a k názvu metody byl připojen asynchronní . - Asynchronní třída HttpClient se používá místo synchronní třídy WebClient .
- Klíčové slovo await bylo použito na asynchronní metodu HttpClientGetAsync .
Následující obrázek znázorňuje asynchronní zobrazení gizmo.
Prezentace dat gizmos v prohlížečích je identická se zobrazením vytvořeným synchronním voláním. Jediným rozdílem je, že asynchronní verze může být výkonnější při velkém zatížení.
RegisterAsyncTask Notes
Metody, ke kterým se připojíte, RegisterAsyncTask
se spustí okamžitě po prerenderu.
Pokud používáte události stránky async void přímo, jak je znázorněno v následujícím kódu:
protected async void Page_Load(object sender, EventArgs e) {
await ...;
// do work
}
už nemáte úplnou kontrolu nad tím, kdy se události spustí. Pokud například .aspx i . Hlavní definované Page_Load
události a jedna nebo obě jsou asynchronní, není možné zaručit pořadí provádění. Platí stejné neurčité pořadí pro obslužné rutiny událostí (například async void Button_Click
).
Provádění více operací paralelně
Asynchronní metody mají významnou výhodu oproti synchronním metodám, když akce musí provést několik nezávislých operací. V uvedeném příkladu zobrazí synchronní stránka PWG.aspx (pro produkty, widgety a Gizmos) výsledky tří volání webových služeb pro získání seznamu produktů, widgetů a gizmos. Projekt webového rozhraní API ASP.NET , který poskytuje tyto služby, používá Task.Delay k simulaci latence nebo pomalých síťových volání. Pokud je zpoždění nastaveno na 500 milisekund, asynchronní stránka PWGasync.aspx trvá dokončení o něco více než 500 milisekund, zatímco synchronní PWG
verze trvá více než 1500 milisekund. Synchronní stránka PWG.aspx je zobrazena v následujícím kódu.
protected void Page_Load(object sender, EventArgs e)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var widgetService = new WidgetService();
var prodService = new ProductService();
var gizmoService = new GizmoService();
var pwgVM = new ProdGizWidgetVM(
widgetService.GetWidgets(),
prodService.GetProducts(),
gizmoService.GetGizmos()
);
WidgetGridView.DataSource = pwgVM.widgetList;
WidgetGridView.DataBind();
ProductGridView.DataSource = pwgVM.prodList;
ProductGridView.DataBind();
GizmoGridView.DataSource = pwgVM.gizmoList;
GizmoGridView.DataBind();
stopWatch.Stop();
ElapsedTimeLabel.Text = String.Format("Elapsed time: {0}",
stopWatch.Elapsed.Milliseconds / 1000.0);
}
Asynchronní PWGasync
kód na pozadí je zobrazen níže.
protected void Page_Load(object sender, EventArgs e)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
RegisterAsyncTask(new PageAsyncTask(GetPWGsrvAsync));
stopWatch.Stop();
ElapsedTimeLabel.Text = String.Format("Elapsed time: {0}",
stopWatch.Elapsed.Milliseconds / 1000.0);
}
private async Task GetPWGsrvAsync()
{
var widgetService = new WidgetService();
var prodService = new ProductService();
var gizmoService = new GizmoService();
var widgetTask = widgetService.GetWidgetsAsync();
var prodTask = prodService.GetProductsAsync();
var gizmoTask = gizmoService.GetGizmosAsync();
await Task.WhenAll(widgetTask, prodTask, gizmoTask);
var pwgVM = new ProdGizWidgetVM(
widgetTask.Result,
prodTask.Result,
gizmoTask.Result
);
WidgetGridView.DataSource = pwgVM.widgetList;
WidgetGridView.DataBind();
ProductGridView.DataSource = pwgVM.prodList;
ProductGridView.DataBind();
GizmoGridView.DataSource = pwgVM.gizmoList;
GizmoGridView.DataBind();
}
Následující obrázek znázorňuje zobrazení vrácené z asynchronní stránky PWGasync.aspx .
Použití tokenu zrušení
Asynchronní metody vracející Task
se jsou zrušitelné, to znamená, že přebírají CancellationToken parametr, pokud je k dispozici s atributem AsyncTimeout
Page direktivy. Následující kód ukazuje stránku GizmosCancelAsync.aspx s časovým limitem sekundy.
<%@ Page Async="true" AsyncTimeout="1"
Language="C#" AutoEventWireup="true"
CodeBehind="GizmosCancelAsync.aspx.cs"
Inherits="WebAppAsync.GizmosCancelAsync" %>
Následující kód ukazuje soubor GizmosCancelAsync.aspx.cs .
protected void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(GetGizmosSvcCancelAsync));
}
private async Task GetGizmosSvcCancelAsync(CancellationToken cancellationToken)
{
var gizmoService = new GizmoService();
var gizmoList = await gizmoService.GetGizmosAsync(cancellationToken);
GizmosGridView.DataSource = gizmoList;
GizmosGridView.DataBind();
}
private void Page_Error(object sender, EventArgs e)
{
Exception exc = Server.GetLastError();
if (exc is TimeoutException)
{
// Pass the error on to the Timeout Error page
Server.Transfer("TimeoutErrorPage.aspx", true);
}
}
V ukázkové aplikaci, která je k dispozici, výběr odkazu GizmosCancelAsync zavolá stránku GizmosCancelAsync.aspx a demonstruje zrušení (vypršením časového limitu) asynchronního volání. Vzhledem k tomu, že doba zpoždění je v náhodném rozsahu, možná budete muset stránku několikrát aktualizovat, aby se zobrazila chybová zpráva o vypršení časového limitu.
Konfigurace serveru pro volání webové služby s vysokou souběžností nebo vysokou latencí
Abyste mohli využít výhod asynchronní webové aplikace, možná budete muset provést určité změny ve výchozí konfiguraci serveru. Při konfiguraci a zátěžovém testování asynchronní webové aplikace mějte na paměti následující skutečnosti.
Windows 7, Windows Vista, Windows 8 a všechny klientské operační systémy Windows mají maximálně 10 souběžných požadavků. Abyste viděli výhody asynchronních metod při vysokém zatížení, budete potřebovat operační systém Windows Server.
Pomocí následujícího příkazu zaregistrujte .NET 4.5 ve službě IIS z příkazového řádku se zvýšenými oprávněními:
%windir%\Microsoft.NET\Framework64 \v4.0.30319\aspnet_regiis -i
Viz ASP.NET nástroj pro registraci služby IIS (Aspnet_regiis.exe).Možná budete muset zvýšit limitHTTP.sys fronty z výchozí hodnoty 1 000 na 5 000. Pokud je nastavení příliš nízké, může se zobrazit HTTP.sys zamítnutí požadavků se stavem HTTP 503. Změna limitu HTTP.sys fronty:
- Otevřete Správce služby IIS a přejděte do podokna Fondy aplikací.
- Klikněte pravým tlačítkem na cílový fond aplikací a vyberte Upřesnit nastavení.
- V dialogovém okně Upřesnit nastavení změňte délku fronty z 1 000 na 5 000.
Všimněte si, že v imagích výše je rozhraní .NET Framework uvedené jako v4.0, i když fond aplikací používá .NET 4.5. Pokud chcete porozumět této nesrovnalosti, podívejte se na následující informace:
Správa verzí .NET a cílení na více verzí – .NET 4.5 je místní upgrade na .NET 4.0
Nastavení aplikace nebo fondu aplikací služby IIS pro použití ASP.NET 3.5 místo verze 2.0
Pokud vaše aplikace používá webové služby nebo System.NET ke komunikaci s back-endem přes protokol HTTP, možná budete muset zvětšit element connectionManagement/maxconnection . U ASP.NET aplikací je tato možnost omezena funkcí automatické konfigurace na 12násobek počtu procesorů. To znamená, že na čtyřúhelníku můžete mít maximálně 12 × 4 = 48 souběžných připojení ke koncovému bodu IP. Vzhledem k tomu, že je to vázáno na autoConfig, nejjednodušší způsob, jak zvýšit
maxconnection
ASP.NET aplikace, je nastavit System.Net.ServicePointManager.DefaultConnectionLimit programově v metodě fromApplication_Start
v souboru global.asax . Příklad najdete ve stažení ukázky.V .NET 4.5 by výchozí hodnota 5000 pro MaxConcurrentRequestsPerCPU měla být v pořádku.