Partager via


Services en arrière-plan et tâches de démarrage

Lors de la génération de Orleans applications, vous devez souvent effectuer des opérations en arrière-plan ou initialiser des composants au démarrage de l’application.

Les tâches de démarrage peuvent être utilisées pour effectuer le travail d’initialisation lorsqu’un silo démarre, avant ou après qu’il commence à accepter des demandes. Les cas d’usage courants sont les suivants :

  • Initialisation de l’état du grain ou préchargement des données
  • Configuration des connexions de service externe
  • Exécution des migrations de base de données
  • Validation de la configuration
  • Réchauffement des caches

L’approche recommandée consiste à utiliser .NET BackgroundService ou IHostedService. Pour plus d’informations, consultez la documentation sur les tâches en arrière-plan avec les services hébergés dans ASP.NET Core.

Voici un exemple qui effectue un ping sur un grain toutes les 5 secondes :

public class GrainPingService : BackgroundService
{
    private readonly IGrainFactory _grainFactory;
    private readonly ILogger<GrainPingService> _logger;

    public GrainPingService(
        IGrainFactory grainFactory,
        ILogger<GrainPingService> logger)
    {
        _grainFactory = grainFactory;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    _logger.LogInformation("Pinging grain...");
                    var grain = _grainFactory.GetGrain<IMyGrain>("ping-target");
                    await grain.Ping();
                }
                catch (Exception ex) when (ex is not OperationCanceledException)
                {
                    // Log the error but continue running
                    _logger.LogError(ex, "Failed to ping grain. Will retry in 5 seconds.");
                }

                await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
            }
        }
        catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
        {
            // Ignore cancellation during shutdown.
        }
        finally
        {
            _logger.LogInformation("Grain ping service is shutting down.");
        }
    }
}

L’ordre d’inscription est important, car les services ajoutés au générateur d’hôtes sont démarrés un par un, dans l’ordre dans lequel ils sont inscrits. Vous pouvez enregistrer le service d’arrière-plan comme suit :

var builder = WebApplication.CreateBuilder(args);

// Configure Orleans first
builder.UseOrleans(siloBuilder => 
{
    // Orleans configuration...
});

// Register the background service after calling 'UseOrleans' to make it start once Orleans has started.
builder.Services.AddHostedService<GrainPingService>();

var app = builder.Build();

Le service en arrière-plan démarre automatiquement lorsque l’application démarre et s’arrête correctement lorsque l’application s’arrête.

Utilisation d’IHostedService

Pour des scénarios plus simples où vous n’avez pas besoin d’une opération en arrière-plan continue, vous pouvez implémenter IHostedService directement :

public class GrainInitializerService : IHostedService
{
    private readonly IGrainFactory _grainFactory;
    private readonly ILogger<GrainInitializerService> _logger;

    public GrainInitializerService(
        IGrainFactory grainFactory,
        ILogger<GrainInitializerService> logger)
    {
        _grainFactory = grainFactory;
        _logger = logger;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Initializing grains...");
        var grain = _grainFactory.GetGrain<IMyGrain>("initializer");
        await grain.Initialize();
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

Inscrivez-le de la même façon :

builder.Services.AddHostedService<GrainInitializerService>();

Orleans Tâches de démarrage

Remarque

Bien que les tâches de démarrage soient toujours prises en charge, nous vous recommandons d’utiliser BackgroundService ou IHostedService à la place, car il s’agit du mécanisme d’hébergement .NET courant pour exécuter des tâches en arrière-plan.

Avertissement

Toute exception lancée à partir d'une tâche de démarrage sera signalée dans le journal du silo et arrêtera le silo. Cette approche 'fail-fast' permet de détecter les problèmes de configuration et de démarrage pendant les tests plutôt que de causer des problèmes inattendus ultérieurement, mais celle-ci peut également signifier que les échecs temporaires dans une tâche de démarrage entraînent l’indisponibilité de l’hôte.

Si vous devez utiliser le système de tâches de démarrage intégré, vous pouvez les configurer comme suit :

Inscrire un délégué

Un délégué peut être enregistré comme tâche de démarrage en utilisant la méthode d'extension AddStartupTask appropriée sur ISiloBuilder.

siloBuilder.AddStartupTask(
    async (IServiceProvider services, CancellationToken cancellation) =>
    {
        var grainFactory = services.GetRequiredService<IGrainFactory>();
        var grain = grainFactory.GetGrain<IMyGrain>("startup-task-grain");
        await grain.Initialize();
    });

Enregistrer une implémentation de IStartupTask

L’interface IStartupTask peut être implémentée et inscrite en tant que tâche de démarrage à l’aide de la méthode d’extension AddStartupTask sur ISiloBuilder.

public class CallGrainStartupTask : IStartupTask
{
    private readonly IGrainFactory _grainFactory;

    public CallGrainStartupTask(IGrainFactory grainFactory) =>
        _grainFactory = grainFactory;

    public async Task Execute(CancellationToken cancellationToken)
    {
        var grain = _grainFactory.GetGrain<IMyGrain>("startup-task-grain");
        await grain.Initialize();
    }
}

Inscrivez la tâche de démarrage comme suit :

siloBuilder.AddStartupTask<CallGrainStartupTask>();