Async / await

Een veel gemaakte fout is het verwarren van async & await met multithreading of parallel programming. Het asyc/await patroon heeft niets te maken met het verplaatsen van werk naar een andere thread. In deze blog geef ik een samenvatting van het gebruik van async & await op de backend. Dit patroon helpt je met het maken van schaalbare web applicaties.

Een webserver heeft een maximaal aantal threads waarin inkomende requests afgehandeld kunnen worden (gelimiteerd door het aantal resources van de machine (CPU, RAM etc)).

Een request bestaat uit een aantal taken welke afgehandeld moeten worden, zoals:

  • Parsen inkomende POST (JSON)
  • Kiezen welke route uitgevoerd moet worden
  • Uitvoeren van code binnen methode
    • Queryen van een database
    • Resultaat van de call naar de database bewerken op basis van inkomende POST data
    • Opslaan van het resultaat in een database
  • Resultaat terugsturen naar browser (JSON serializer)

Veel van deze taken moeten door de webserver zelf afgehandeld worden. Wanneer we geen gebruik maken van async/await ziet het er ongeveer zo uit:

Zodra een request op de webserver binnen komt maakt ASP.NET een thread aan waarin de executie van de code plaats zal vinden. Zoals eerder aangegeven heeft een webserver een maximaal aantal threads waarin requests afgehandeld kunnen worden, en in deze situatie wordt de thread gedurende de hele request gebruikt. Een thread die in gebruik is kan niet door andere requests gebruikt worden. Wanneer er genoeg requests binnen komen om het maximum van de server te bereiken zal een queue ontstaan waardoor gebruikers moeten wachten totdat de server ‘tijd’ heeft.

In bovenstaand schema staan twee onderdelen welke niet door de webserver uitgevoerd worden, namelijk de ‘Database Query’ en de ‘Database Save’. Op het moment dat een commando naar de database server wordt gestuurd zal onze code wachten totdat de database server klaar is en het resultaat kan terugsturen. Dit wachten noemen we een ‘blocking operation’. We blokkeren namelijk onze eigen thread en wachten totdat de externe dienst (in dit geval een database server) klaar is om informatie terug te sturen. In sommige situaties kan dit lang duren (secondes), terwijl onze thread op dat moment niets aan het doen is.

Met async en await kunnen we ervoor zorgen dat de tijd waarin we op een externe dienst moeten wachten ingezet kan worden voor andere taken, zoals nieuwe requests die op de server binnen komen. In plaats van wachten totdat de ander klaar is kunnen we verder gaan met nieuwe dingen.

Zodra de externe dienst een resultaat voor ons heeft wordt de originele thread hersteld en loopt de code verder:

Het async/await framework van het .NET Framework zorgt ervoor dat we zelf niet na hoeven denken over de afhandeling van deze wisselingen van threads. Op het moment dat het resultaat van de externe dienst binnen is loopt onze code door alsof het nooit gepauzeerd is.

De impact op de schaalbaarheid van de applicatie is natuurlijk afhankelijk van de hoeveelheid externe diensten die aangeroepen worden. Tegenwoordig maken we steeds meer gebruik van cloud-diensten en externe servers, zoals:

  • Databases
  • Opslag van bestanden
  • Authenticatie

De volgende operaties kunnen we ook zien als extern:

  • Het schrijven naar en ophalen van disks
  • Het versturen van een mail (SMTP communicatie)
  • Alle externe HTTP requests (webservices etc)

Eigenlijk alle I/O operaties (disk, netwerk, USB etc) zijn een goede kandidaat om async te gebruiken.

Als bovenstaande request 50ms zou duren waarvan 20ms gewacht moet worden op externe diensten, en we de wachttijd in kunnen zetten voor nieuwe requests in plaats van puur te wachten, kan de server theoretisch 40% meer requests afhandelen door gebruik te maken van async/await. Let op, het aanmaken van nieuwe threads en het synchroniseren van de context die hiervoor nodig is kost ook tijd. Het is dus niet zo dat je er netto 40% op vooruit gaat.

Let daarom altijd goed op wanneer je aan de backend werkt en methodes aanroept. Als het om een externe dienst gaat is de kans heel groot dat die methode ook een async variant heeft.

Meer uitleg over dit onderwerp kun je vinden op de Microsoft Documentatie site.

Mocht je vragen hebben over async/await, of multithreading op een webserver, neem gerust contact met me op.

Over de auteur

Floris Robbemont

Application/Solutions Developer/Architect, (Lead) Web Developer/Architect, Database Administrator/Developer, Umbraco Developer.

I'm always up for a challenge, technical or otherwise. Ready to learn new techniques.

MAAK KENNIS MET ANKO

Benieuwd wat we voor jou kunnen betekenen?
Ik maak graag vrijblijvend kennis!

Op deze website gebruiken we tools om de website te verbeteren en je een goede, gepersonaliseerde ervaring te geven. Ga je daar mee akkoord?