Oussama SAIDI

0 %
Oussama SAIDI
Consultant .Net
Sharepoint Office 365

  • Résidence:
    France
  • Ville:
    Lille
Arabe
Français
Anglais
.Net
Html/Css/Js
Web Api
Base de données
  • C#,.Net Core, .Net MVC
  • Rest Api
  • Docker
  • GIT,TFS

Hangfire avec ASP.NET Core

octobre 20, 2022

Presque toutes les applications doivent effectuer une sorte de travail de fond. Qu’il s’agisse d’une tâche longue et fastidieuse ou d’un travail répétitif que nous devons faire tous les deux jours, dans cet article, nous allons apprendre comment nous pouvons facilement l’implémenter. Pour cela, nous allons utiliser Hangfire – une bibliothèque open source pour ASP.NET Core .

Pour télécharger le code source de cet article, vous pouvez visiter notre référentiel Hangfire avec ASP.NET Core .

Introduction aux tâches en arrière-plan

Pour assurer le bon fonctionnement de nos applications et la satisfaction de nos utilisateurs, nous pouvons utiliser pour certaines tâches un concept appelé traitement en arrière-plan. Une tâche est une méthode dans notre code avec une logique que nous devons exécuter. Le traiter en arrière-plan signifie que nous pouvons l’exécuter en dehors du thread principal de l’application.

Quand devrions-nous utiliser le traitement en arrière-plan ?

Il existe de nombreuses situations dans lesquelles nous devons garder un travail spécifique loin des projecteurs.

Parfois, il s’agit d’une tâche de longue haleine dont l’affichage des données de l’interface utilisateur ne dépend pas et qui ralentit le flux de l’application. Un exemple pourrait être de télécharger un tas de photos sur une plate-forme de médias sociaux. Nous ne voudrions pas que cette action empêche nos utilisateurs d’utiliser la plateforme pendant que nous traitons les photos. Nous pouvons effectuer la tâche de téléchargement dans les coulisses et l’utilisateur peut continuer à naviguer librement.

Nous pouvons également avoir besoin de planifier certaines tâches pour qu’elles s’exécutent toutes les heures, toutes les semaines ou peut-être tous les mois. Comme surveiller les utilisateurs inactifs ou envoyer des factures d’abonnement. Ou nous devrons peut-être envoyer une notification à notre utilisateur une fois, sauf pas maintenant, mais dans environ trois jours.

Nous pouvons gérer toutes ces tâches avec un bon planificateur de tâches en arrière-plan qui s’occupe des détails pour nous. C’est là que Hangfire brille.

Qu’est-ce que Hangfire ?

Hangfire est un planificateur de tâches open source et bien documenté pour ASP.NET et ASP.NET Core. Il est multithread, facilement évolutif et offre une variété de types de travaux. Il est bien structuré, simple à utiliser et offre des performances puissantes.

Caractéristiques les plus remarquables de Hangfire

Comparé aux autres planificateurs disponibles, Hangfire offre de nombreux avantages. Entre autres choses, il est particulièrement facile à installer et à configurer, il utilise un stockage persistant et il dispose d’un joli tableau de bord d’interface utilisateur pour vérifier nos travaux à tout moment. Il prend en charge le traitement de plusieurs files d’attente et nous pouvons également choisir explicitement la file d’attente que nous voulons utiliser pour un travail spécifique.

Parce que le stockage persistant enregistre l’état du travail, nous avons également un grand bonus – les tentatives de travail. Cette fonctionnalité permet de s’assurer que nos travaux finissent de s’exécuter même s’ils rencontrent une exception transitoire ou si le pool d’applications dédiée tombe en panne. Si une tâche échoue, Hangfire essaiera de l’exécuter à nouveau dès que possible.

Comment fonctionne Hangfire ?

L’architecture Hangfire comporte trois composants principaux : le client, le serveur et le stockage. Ils sont étroitement liés dans l’ensemble du processus et dépendent les uns des autres.

Le rôle de chaque composant

Voyons ce dont chaque composant est responsable :

  • Client Hangfire – Ce sont les bibliothèques réelles de notre application. Le client crée le travail, sérialise sa définition et s’assure de le stocker dans notre stockage persistant.
  • Stockage Hangfire – Ceci est notre base de données. Il utilise quelques tables désignées que Hangfire crée pour nous. Il stocke toutes les informations sur nos tâches – définitions, statut d’exécution, etc. Hangfire prend en charge les options RDBMS et NoSQL, nous pouvons donc choisir celle qui convient à notre projet. Par défaut, il utilise SQL Server, mais toute autre option prise en charge est également facile à configurer.
  • Serveur Hangfire – Le serveur a pour tâche de récupérer les définitions de travail du stockage et de les exécuter. Il est également responsable de garder notre stockage de travail propre de toutes les données que nous n’utilisons plus. Le serveur peut vivre dans notre application ou sur un autre serveur. Il pointe toujours vers le stockage de la base de données donc son emplacement ne joue pas de rôle, mais cette diversité peut être très utile.

Étant donné que Hangfire est conçu de manière aussi générique que possible, nous pouvons également étendre manuellement certaines de ses parties telles que la mise en œuvre du stockage, la création de tâches ou les processus d’activation de tâches pour répondre à des besoins spécifiques.

Le flux de travail Hangfire

Le flux de travail entre les composants est assez simple :

Une fois que nous avons spécifié notre tâche dans le code et appelé la méthode Hangfire appropriée pour la créer, le client Hangfire crée la tâche et la stocke dans la base de données. Le contrôle revient ensuite à l’application afin que le thread principal puisse poursuivre son travail.
Lorsqu’un travail se trouve dans le stockage, le serveur le récupère et crée un thread d’arrière-plan pour traiter le travail récupéré.

On peut représenter ce flux avec quatre actions consécutives dans deux branches différentes – une entre client et stockage et l’autre entre stockage et serveur :

Hangfire avec ASP.NET Core

Configuration de Hangfire dans ASP.NET Core

Après avoir appris les mécanismes de l’architecture Hangfire, voyons comment cela fonctionne dans la pratique.

Pour commencer, créons un nouveau projet d’API Web ASP.NET Core :

Hangfire avec ASP.NET Core

Nous utiliserons également Swagger pour envoyer des requêtes HTTP. Nous pouvons facilement l’ajouter à notre .NET 5.0. projet si nous cochons la case Activer le support OpenAPI :

Hangfire avec ASP.NET Core

Paquet Hangfire NuGet

Tout d’abord, nous allons installer le package Hangfire NuGet. Nous pouvons rechercher Hangfire dans le gestionnaire de paquets :

Hangfire avec ASP.NET Core

ou exécutez la commande dans la console du gestionnaire de packages :

PM> Install-Package Hangfire

Après avoir installé ce package, il référence par défaut d’autres packages nécessaires tels que Hangfire.Core et Hangfire.SqlServer et vous n’avez pas à vous en soucier.
L’exception est si vous utilisez une option de stockage différente de SQL Server. Dans ce cas, assurez-vous d’ajouter le package NuGet supplémentaire pour ce type de stockage.

Stockage persistant

Pour stocker les définitions et les statuts des tâches, comme nous l’avons maintenant appris, Hangfire peut utiliser une base de données SQL Server par défaut et nous pouvons également choisir d’autres options. Dans notre exemple de projet, par souci de simplicité, nous allons utiliser le stockage MSSQL LocalDB.

Quel que soit le type de base de données que nous utilisons, locale ou distante, nous devrions avoir une définition de base de données – même si elle est vide. Hangfire créera les tables dont nous avons besoin pour le stockage des tâches, mais il ne peut pas créer de base de données, nous devons donc la fournir.

Après avoir configuré notre base de données locale, nous devons mettre à jour le fichier appsettings.json :

"AllowedHosts": "*",
    "ConnectionStrings": {
        "DBConnection": "Server=(localdb)\\MSSQLLocalDB;Database=HangfireApplication;Trusted_Connection=True"
    }

Dans l’objet ConnectionStrings, nous ajoutons notre chaîne de connexion LocalDB. Si vous avez décidé d’utiliser une base de données déjà existante ou simplement une alternative à LocalDB, vous devez modifier la chaîne de connexion pour votre cas spécifique.

Classe de démarrage

Après avoir installé le package Hangfire et configuré la base de données, nous devons également configurer notre classe Startup :

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "HangfireApplication", Version = "v1" });
    });
    services.AddHangfire(x =>
    {
        x.UseSqlServerStorage(Configuration.GetConnectionString("DBConnection"));
    });
    services.AddHangfireServer();
}

Dans la ConfigureServicesméthode, avec la AddHangfire()méthode, nous ajoutons le service Hangfire avec l’option de stockage SQL Server. Nous fournissons la chaîne de connexion que nous avons spécifiée dans le fichier appsettings.json.
Nous ajoutons également le serveur Hangfire avec la AddHangfireServer()méthode. Le serveur Hangfire de notre projet va vivre à l’intérieur de l’application.

De plus, nous devons mettre à jour notre pipeline de traitement des demandes :

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "HangfireApplication v1"));
    }
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
    app.UseHangfireDashboard();
}

Dans la Configureméthode, nous ajoutons le tableau de bord Hangfire à notre pipeline, en appelant la UseHangfireDashboard()méthode, afin que nous puissions facilement surveiller nos travaux ultérieurement.

Ce tableau de bord est, par défaut, configuré pour être accessible uniquement localement, mais si vous souhaitez y accéder à distance, vous pouvez également le configurer. Cependant, il expose de nombreuses informations sensibles, soyez donc toujours prudent avec cette configuration.

Lorsque nous exécutons notre application, Hangfire vérifiera automatiquement notre stockage désigné pour les tables nécessaires, et si elles n’existent pas encore, il les créera pour nous :

Hangfire avec ASP.NET Core

C’est tout en ce qui concerne la configuration et nous sommes tous prêts à commencer à utiliser Hangfire. Inspectons maintenant rapidement le tableau de bord Hangfire, puis voyons comment créer des tâches en arrière-plan.

Tableau de bord Hangfire

Si nous démarrons notre application et naviguons vers l’URL https://localhost:5001/hangfire , nous pouvons voir le tableau de bord très utile inclus :

Hangfire avec ASP.NET Core

Dans le tableau de bord, nous pouvons voir toutes les tâches en cours et planifiées que nous créons avec notre client Hangfire. Nous pouvons également surveiller les serveurs, les tentatives de travail, les travaux échoués et garder un œil sur tous les travaux dans la file d’attente. Une autre grande chose que nous pouvons faire dans le tableau de bord est que nous pouvons déclencher manuellement toutes les tâches existantes.

Nous couvrirons les autres onglets un peu plus tard lorsque nous essaierons les différents types de travaux pris en charge par Hangfire, mais pour l’instant, vérifions rapidement l’ onglet Serveurs :

Hangfire avec ASP.NET Core

Ici, nous pouvons voir le serveur qui s’exécute dans notre application de test. Si nous avions plus d’instances de serveur en cours d’exécution, nous les verrions également répertoriées ici. Hangfire vérifie périodiquement l’utilisation du serveur, donc s’il y a un serveur qui n’est plus utilisé, il sera automatiquement supprimé de la liste.

Types d’emplois en arrière-plan dans Hangfire

Hangfire prend en charge différents types de tâches que nous pouvons utiliser pour différentes occasions. Des tâches ponctuelles aux tâches quotidiennes, nous sommes couverts. Voyons comment les mettre en œuvre facilement et vérifier leur exécution dans le tableau de bord.

Pour commencer à tester la planification des tâches, créons un dossier Services avec une classe JobTestService et son interface d’appartenance :

public interface IJobTestService
{
    void FireAndForgetJob();
    void ReccuringJob();
    void DelayedJob();
    void ContinuationJob();
}

Tout d’abord, nous spécifions l’ interface IJobTestService où nous enregistrons quatre méthodes différentes – nommées de manière pratique d’après les types de travaux Hangfire.

Après cela, nous pouvons passer à la modification de la classe de service :

public class JobTestService : IJobTestService
{
    public void FireAndForgetJob()
    {
        Console.WriteLine("Hello from a Fire and Forget job!");
    }
    public void ReccuringJob()
    {
        Console.WriteLine("Hello from a Scheduled job!");
    }
    public void DelayedJob()
    {
        Console.WriteLine("Hello from a Delayed job!");
    }
    public void ContinuationJob()
    {
        Console.WriteLine("Hello from a Continuation job!");
    }
}

Notre classe JobTestService implémente l’ interface IJobTestService que nous avons créée. Chaque méthode de la classe n’imprime que du texte sur notre console, au lieu de faire le travail réel comme elle le ferait dans une application réelle.

Dans le monde réel, à l’intérieur de méthodes comme celles-ci, il peut y avoir un code qui télécharge une photo ou vérifie l’inactivité de l’utilisateur. Dans notre exemple, les appels simples Console.WriteLine()nous permettront de nous concentrer davantage sur la planification des travaux.

Après avoir défini la classe de service, nous devons à nouveau modifier notre classe Startup :

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "HangfireApplication", Version = "v1" });
    });
            
    services.AddScoped<IJobTestService, JobTestService>();
    services.AddHangfire(x =>
    {
        x.UseSqlServerStorage(Configuration.GetConnectionString("DBConnection"));
    });
    services.AddHangfireServer();
}

Pour pouvoir utiliser notre nouveau service avec injection de dépendances, à l’intérieur de la ConfigureServices()méthode, nous enregistrons notre implémentation d’interface à l’aide de la AddScoped()méthode.

Emplois Fire and Forget

Le premier type de travail que nous allons couvrir est le travail Fire and Forget. Lorsque nous créons ce type de job, le serveur ne l’exécute qu’une seule fois, dès qu’il le peut.

Dans le dossier Controllers de notre application, nous pouvons créer et modifier un fichier JobTestController.cs :

[Route("api/[controller]")]
[ApiController]
public class JobTestController : ControllerBase
{
    private readonly IJobTestService _jobTestService;
    private readonly IBackgroundJobClient _backgroundJobClient;
    public JobTestController(IJobTestService jobTestService, IBackgroundJobClient backgroundJobClient)
    {
        _jobTestService = jobTestService;
        _backgroundJobClient = backgroundJobClient;
    }

Nous créons le constructeur JobTestController et injectons l’ interface IBackgroundJobClient fournie par Hangfire. Nous utiliserons les méthodes de cette interface pour planifier différents types de tâches. En plus de cela, nous injectons également l’ interface IJobTestService que nous avons créée.

Ajoutons maintenant un point de terminaison de test qui nous aidera à planifier la tâche :

[HttpGet("/FireAndForgetJob")]
public ActionResult CreateFireAndForgetJob()
{
    _backgroundJobClient.Enqueue(() => _jobTestService.FireAndForgetJob());
    return Ok();
}

Notre nouveau point de terminaison utilise l’interface Hangfire injectée pour planifier ce type de tâche. Nous appelons la méthode de l’interface Enqueue()et lui transmettons notre FireAndForgetJob()appel de méthode de service en tant que paramètre.
Cette méthode pratique de mise en file d’ attente prend soin de créer et d’enregistrer la définition de tâche dans notre stockage et, bien sûr, de la mettre en file d’attente pour le traitement.

Maintenant, nous pouvons envoyer une requête GET à ce point de terminaison en utilisant Swagger, et par conséquent, cela va créer notre premier travail avec Hangfire !

Après avoir envoyé la demande, vérifions le travail dans le tableau de bord. Accédez à l’ onglet Jobs , où il devrait maintenant être visible sous la section Succeeded :

Hangfire avec ASP.NET Core

Ici, nous pouvons réexécuter manuellement n’importe quel travail réussi si nous le souhaitons, avec le bouton Remettre en file d’attente les travaux .

Tâches retardées

Les tâches retardées sont celles que nous voulons sûrement exécuter, mais pas tout de suite. Nous pouvons les programmer à une certaine heure, peut-être dans une minute ou dans trois mois.

Pour tester ce type de job, créons un nouveau endpoint dans notre JobTestController :

[HttpGet("/DelayedJob")]
public ActionResult CreateDelayedJob()
{
    _backgroundJobClient.Schedule(() => _jobTestService.DelayedJob(), TimeSpan.FromSeconds(60));
    return Ok();
}

Nous ajoutons un point de CreateDelayedJob()terminaison, où nous utilisons à nouveau l’ interface IBackgroundJobClient . Cette fois, cependant, nous n’appelons pas la Enqueue()méthode. Au lieu de cela, nous utilisons la Schedule()méthode et nous lui passons deux paramètres – notre DelayedJob()appel de méthode de service et le délai.

La Schedule()méthode créera la définition de la tâche et l’enregistrera, mais elle s’assurera également de la planifier dans la file d’attente à l’heure spécifiée.

Désormais, lorsque nous envoyons une requête GET à ce point de terminaison, nous planifions une tâche 60 secondes dans le futur. Il s’affiche dans la section Planifiée de l’ onglet Tâches du tableau de bord :

Hangfire avec ASP.NET Core

Une fois exécuté avec succès, il passe à l’ onglet Réussi .

Emplois récurrents

Nous planifions nos tâches récurrentes afin qu’elles puissent se répéter à un certain intervalle. Pour ces types de tâches, Hangfire utilise l’utilitaire logiciel CRON.

Pour planifier ce travail, nous aurons besoin d’une interface Hangfire différente, alors modifions d’abord le constructeur de notre contrôleur :

[Route("api/[controller]")]
[ApiController]
public class JobTestController : ControllerBase
{
    private readonly IJobTestService _jobTestService;
    private readonly IBackgroundJobClient _backgroundJobClient;
    private readonly IRecurringJobManager _recurringJobManager;
    public JobTestController(IJobTestService jobTestService, IBackgroundJobClient backgroundJobClient, IRecurringJobManager recurringJobManager)
    {
        _jobTestService = jobTestService;
        _backgroundJobClient = backgroundJobClient;
        _recurringJobManager = recurringJobManager;
    }

Nous injectons maintenant également l’interface Hangfire IReccuringJobManager dans le constructeur. Il expose des méthodes utiles pour gérer les tâches récurrentes et nous l’utiliserons pour planifier notre nouvelle tâche.

Nous pouvons maintenant créer un nouveau point de terminaison pour tester la tâche :

[HttpGet("/ReccuringJob")]
public ActionResult CreateReccuringJob()
{
    _recurringJobManager.AddOrUpdate("jobId", () => _jobTestService.ReccuringJob(), Cron.Minutely);
    return Ok();
}

Nous utilisons la méthode de l’interface nouvellement injectée AddOrUpdate(). À cette méthode, nous transmettons un identifiant de travail de choix, notre RecurringJob()appel de méthode et l’intervalle CRON.
La méthode hangfire créera une nouvelle tâche avec l’identifiant spécifié ou mettra à jour une tâche existante. Dans cet exemple, nous allons simplement créer le travail pour la première fois.

Après avoir envoyé une requête GET au point de terminaison à l’aide de Swagger, notre travail récurrent devient visible sous l’ onglet Travaux récurrents du tableau de bord :

Hangfire avec ASP.NET Core

Bien que cela montre que nous avons programmé notre travail pour qu’il se répète à l’heure spécifiée, chaque fois que le serveur le traite, l’instance traitée devient visible dans la section Réussite de l’ onglet Travaux .

Emplois de continuation

Le dernier type de travail que nous allons couvrir est le travail de continuation. Sa principale caractéristique est d’enchaîner l’exécution des tâches. Avec lui, nous pouvons faire en sorte que deux tâches s’exécutent l’une après l’autre en continu.

Nous avons déjà une méthode pour ce type de travail préparée dans notre classe de service, modifions donc notre contrôleur :

[HttpGet("/ContinuationJob")]
public ActionResult CreateContinuationJob()
{
    var parentJobId = _backgroundJobClient.Enqueue(() => _jobTestService.FireAndForgetJob());
    _backgroundJobClient.ContinueJobWith(parentJobId, () => _jobTestService.ContinuationJob());
            
    return Ok();
}

Dans le point de CreateContinuationJob()terminaison, nous utilisons à nouveau l’interface Hangfire IBackgroundJobClient . Nous l’utilisons d’abord pour créer un simple travail Fire and Forget – maintenant nous savons comment faire cela :).
Nous voulons que cette tâche soit un déclencheur pour la tâche suivante, nous nous assurons donc de collecter l’identifiant Enqueue()renvoyé par la méthode. Après sa création, nous appelons la méthode de l’interface ContinueJobWith()et transmettons l’ID de travail du travail créé avec notre ContinuationJob()appel de méthode. La ContinueJobWith()méthode fera en sorte d’enchaîner nos deux jobs ensemble.

Après avoir atteint ce point de terminaison avec une requête GET, si nous vérifions la section Succeeded de l’ onglet Jobs du Dashboard, nous pouvons voir que nos deux jobs se sont exécutés l’un après l’autre :

Hangfire avec ASP.NET Core

Super, tout fonctionne !

Conclusion

Nous avons beaucoup appris sur la bibliothèque simple et puissante appelée Hangfire. Nous savons maintenant comment le configurer dans notre application ASP.NET Core et comment l’utiliser pour planifier différents types de tâches. Il est maintenant temps d’aller planifier des tâches en arrière-plan !

Jusqu’au prochain article.

Meilleures salutations.

Dernier articles

Publié dans .Net Core, Asp .Net, C Sharp, c#, Hangfire
© 2022 Tous les droits sont réservés.
oussamasaidi.com