Your web browser is out of date. Update your browser for more security, speed and the best experience on this site.

Een introductie in de .NET library Hangfire

Een digitale applicatie is een radar vol processen die vaak op de achtergrond plaatsvinden. Stijn Roscam, consultant bij Axxes, legt uit hoe Hangfire daar een belangrijke rol in kan spelen.

Deel dit artikel
.NET Software & Services
Axxes Insight Hangfire 01 pdf 1200 x 500 px 3

Soms gebeuren er zaken in een applicatie waar je de gebruikers niet mee lastig wil vallen, maar gelukkig biedt background processing daar een oplossing voor. Dat zijn - de naam zegt het zelf - taken die je op de achtergrond van een applicatie uitvoert. Denk bijvoorbeeld aan het uitsturen van e-mails na een online bestelling. Het kan ook gaan om cron-taken, opdrachten die we op een vast moment of met een bepaalde interval willen uitvoeren. Denk bijvoorbeeld aan het refreshen van je cache of het genereren van maandelijkse rapporten.

Ook resource-intensieve processen, zoals het toevoegen van gebruikersdata aan een dataset waarop we machine learning willen toepassen, kun je via background processing uitvoeren. Vraag dat maar aan Stijn Roscam, die al drie jaar bij Axxes in de .NET league aan de slag is en op Haxx over Hangfire kwam vertellen.

De vier taken in Hangfire

Hangfire is een gratis en open source .NET library die helpt bij die background processing. In de gratis versie zijn er vier mogelijke taken die je op de achtergrond kan uitvoeren: fire-and-forget jobs, delayed jobs, recurring jobs en continuations. Het stelt ons ook in staat om data over uitgevoerde en geplande jobs te behouden wanneer je de applicatie afsluit.

Daarnaast heeft Hangfire ook automatische retries, waardoor het systeem zelf een taak opnieuw zal inplannen wanneer die onverwacht niet is uitgevoerd. Op het bijbehorende dashboard kun je steeds zien waar en waarom iets fout loopt.

Zo werkt Hangfire

Hangfire bestaat uit drie belangrijke componenten: de client, de server en de job storage. De client is de webapplicatie die onze taken die we in de achtergrond willen uitvoeren gaat inplannen. We definiëren welke taken we willen uitvoeren, wat de bijbehorende argumenten zijn, en op welk tijdstip ze uitgevoerd moeten worden. Alle data wordt geserialiseerd en in een database gestoken.

Aan de andere kant zit de Hangfire-server, die continu op de achtergrond in de database zal nagaan of er taken zijn ingepland. Wanneer dat het geval is, zal hij de geserialiseerde data uit de database ophalen en bekijken of hij alle dependencies kan oplossen met zijn DI Container. Vervolgens wordt de taak uitgevoerd, of in de database opnieuw ingepland. Standaard wordt er vanuit Hangfire een SQL Server ondersteund, maar er zijn nog veel third party libraries om bijvoorbeeld met MongoDB of Redis te kunnen verbinden.

Het zou logisch zijn om te denken dat de client een webapplicatie is, de Hangfire Server een aparte webapplicatie en de jobstorage een database die daarbuiten valt. Dat kan werken, maar het kan ook anders. Zo kan een webapplicatie zowel als client als server dienen. Die gaat de taken dan niet alleen inplannen, maar ook uitvoeren.

Door een client en server te scheiden, verloopt het inplannen en uitvoeren van taken in twee volledig aparte processen, wat ervoor zorgt dat ze ook individueel geschaald kunnen worden. Wanneer het bijvoorbeeld gaat over heel resource-intensieve achtergrondtaken, kan de server die de taken uitvoert veel meer resources toegewezen krijgen dan de client. Die heb je in dat geval enkel nodig om de taken in te plannen.

Axxes Insight Hangfire 01 pdf 1200 x 500 px 4

De implementatie van Hangfire en de taken

Wanneer je Hangfire wilt implementeren begin je met de installatie van de NuGet Package, om vervolgens Hangfire aan de startup file toe te voegen. In de documentatie staan een aantal stappen beschreven om configuratie toe te voegen, net als het verbinden met een SQL Database. Als de database nieuw is, zal Hangfire bij de opstart alle nodige tabellen aanmaken. Vervolgens kan je in de webapplicatie taken inplannen en uitvoeren.

Een eerste vorm van taken zijn de Fire and Forget-taken. Deze voer je eenmalig en zo snel mogelijk uit. Wanneer je op een webshop iets koopt, wil je bijvoorbeeld na het plaatsen van je bestelling meteen een bevestigingsmail krijgen. Bij het aanmaken van een taak krijg je een job-ID terug, waarmee je later jobs kan verwijderen.

Delayed-jobs zijn gelijkaardig aan Fire and Forget-taken, maar die worden niet instant uitgevoerd. Je kan ze een delay via een TimeSpan geven, of een DateTimeOffset om te bepalen op welk moment de taak uitgevoerd zou moeten worden. Dankzij een reschedule kan je ingeplande taken nog aanpassen wanneer dat nodig zou zijn.

Ook recurring tasks hebben hun naam niet gestolen, want dit zijn taken die we op intervalbasis uitvoeren. Denk maar aan het refreshen van de cache of het genereren van maandelijkse rapporten. Deze taak genereert geen job-ID, dus die moet je zelf meegeven. Via het trigger commando is het ook mogelijk om deze in één keer uit te voeren en ze rechtstreeks in te plannen. Door dit te doen wordt de taak wel onmiddellijk uitgevoerd, maar blijft ze tegelijkertijd ingepland staan op het voorziene moment. Deze recurring jobs verwijderen zichzelf niet, dat moet je dus manueel doen.

De continuation tasks handelen één background job af nadat een andere succesvol is voltooid. Op die manier kun je er bijvoorbeeld voor zorgen dat een winkelier een melding krijgt nadat een klant de bevestigingsmail heeft ontvangen na een online aankoop.

Axxes Insight Hangfire 01 pdf 1200 x 500 px 5

Het dashboard

Op het dashboard van Hangfire kan je alles over de background processing opvolgen. Zo zie je bijvoorbeeld hoeveel taken succesvol zijn uitgevoerd, gefaald of ingepland. Door op de bijbehorende ID te klikken krijg je meer informatie over de taak in kwestie, en bij de retries zie je de gefaalde taken staan. Zoals eerder gezegd kan je recurring jobs triggeren nog voor het moment waarop ze zijn ingepland, ook dat kan via het dashboard. Er is een tabblad met een overzicht van alle servers die aan de database vasthangen.

Axxes Insight Hangfire 01 pdf 1200 x 500 px 6

De tips van Stijn

Houd je argumenten klein en simpel. Gebruik dus geen complexe objecten om als argument aan je methodes mee te geven, maar werk met pointers naar objecten. Als je dat niet doet zou je alle complexe objecten moeten serialiseren, in de database plaatsen en wanneer je ze ophaalt terug deserialiseren. Bij het inplannen van een taak ga je dan vaak een object meegeven met je argumenten, maar bij het uitvoeren zijn die argumenten soms niet meer geldig waardoor onverwacht gedrag optreedt.

Neem dus een ID mee en sla je objecten op in de database waarop je je background tasks zou willen uitvoeren. Een goed voorbeeld daarvan is het uitsturen van een nieuwsbrief. Het zou onverstandig zijn om bij elke wijziging een background job aan te maken waarin je de nieuwsbrief serialiseert en de inhoud meestuurt. In dat geval zou je bij iedere aanpassing de data moeten overschrijven, een taak verwijderen en een nieuwe taak inplannen omdat het achterliggende argument veranderde. Het is handiger om je nieuwsbrief in een database bij te houden. Dan moet je die taak maar één keer inplannen, en in de database zelf die nieuwsbrief overschrijven en de ID meegeven.

Nog een tip is de “reentrancy” van een applicatie. Stel dat een methode wordt afgebroken tijdens het uitvoeren, moet je ze op een veilige manier opnieuw kunnen oproepen zonder dat er onverwacht gedrag zal opduiken. Om in het voorbeeld van de webshop te blijven: na het aankooporder stuur je een mail naar de klant én daarna rechtstreeks naar de winkelier om de bestelling te plaatsen. Stel dat je een time-out krijgt van een SMTP server bij het versturen van die tweede mail, zal het systeem de taak opnieuw proberen uitvoeren. Die eerste mail is wel gelukt, dus moet eigenlijk niet meer verstuurd worden. Om dat te vermijden moet je een veiligheid inbouwen die in de achtergrond checkt of een van de mails afgeleverd is.

Axxes Insight Hangfire 01 pdf 1200 x 500 px 7

Alternatieven

Binnen .NET zijn er een aantal alternatieven voor Hangfire. De eerste is de BackgroundService die Microsoft heeft ingebouwd. Het is een van de eenvoudigste kits waarmee je aan background processing kan doen. Het is een vrij eenvoudig en toegankelijk systeem. Je kan voor deze methode ook complexe scheduling schrijven, maar dan is het wel meer aangeraden om je scheduling via een library zoals Hangfire of Quartz.NET te laten verlopen.

Quartz.NET is dan weer een open source library die gelijkaardig is aan Hangfire. Het is gebaseerd op Java's Quartz en biedt een flexibele en configureerbare manier om taken te plannen, waaronder het herhaaldelijk uitvoeren van taken met vaste tussenpozen, het uitvoeren van taken op specifieke tijden, en het ondersteunen van complexe cron-taken voor zeer specifieke scheduling vereisten. Quartz.NET integreert naadloos met .NET-applicaties en biedt functionaliteiten zoals transactionele jobs, clustering voor failover en load balancing, en de mogelijkheid om jobs te pauzeren, hervatten of annuleren.

Axxes Insight Hangfire 01 pdf 1200 x 500 px 8

Elke maand onze Insights in jouw mailbox?

Stijn Roscam

Stijn Roscam

.NET Developer

Axxes