.net core 基於 IHostedService 實現定時任務


.net core 基於 IHostedService 實現定時任務

Intro

從 .net core 2.0 開始,開始引入 IHostedService,可以通過 IHostedService 來實現后台任務,但是只能在 WebHost 的基礎上使用。從 .net core 2.1 開始微軟引入通用主機(Generic Host),使得我們可以在不使用 Web 的情況下,也可以使用 IHostedService 來實現 定時任務/Windows服務/后台任務,並且引入了一個 BackgroundService 抽象類來更方便的創建后台任務。

IHostedService

IHostedService 后台任務的執行與應用程序(就此而言,為主機或微服務)的生存期相協調。 當應用程序啟動時注冊任務,當應用程序關閉時,有機會執行某些正常操作或清理。

始終可以啟動后台線程來運行任何任務,而無需使用 IHostedService。 不同之處就在於應用的關閉時間,此時會直接終止線程,而沒有機會執行正常的清理操作。

當注冊 IHostedService 時,.NET Core 會在應用程序啟動和停止期間分別調用 IHostedService 類型的 StartAsync()StopAsync() 方法。 具體而言,即在服務器已啟動並已觸發 IApplicationLifetime.ApplicationStarted 后調用 start。

namespace Microsoft.Extensions.Hosting
{
    //
    // Summary:
    //     Defines methods for objects that are managed by the host.
    public interface IHostedService
    {
        //
        // Summary:
        // Triggered when the application host is ready to start the service.
        Task StartAsync(CancellationToken cancellationToken);
        //
        // Summary:
        // Triggered when the application host is performing a graceful shutdown.
        Task StopAsync(CancellationToken cancellationToken);
    }
}

可以從頭開始創建自定義托管服務類並實現 IHostedService,因為在使用 .NET Core 2.0 時需執行這些操作。

但是,由於大多數后台任務在取消令牌管理和其他典型操作方面都有類似的需求,因此 .net core 2.1 有一個非常方便且可以從中進行派生的抽象基類,BackgroundService 定義如下:

// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts =
                                                   new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it,
        // this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        }
        finally
        {
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
        }

    }

    public virtual void Dispose()
    {
        _stoppingCts.Cancel();
    }
}

實現一個的后台定時任務

基於上面的信息,我們可以基於 IHostedService 實現一個簡單的后台定時任務服務,

public abstract class ScheduledService : IHostedService, IDisposable
{
    private readonly Timer _timer;
    private readonly TimeSpan _period;
    protected readonly ILogger Logger;

    protected ScheduledService(TimeSpan period, ILogger logger)
    {
        Logger = logger;
        _period = period;
        _timer = new Timer(Execute, null, Timeout.Infinite, 0);
    }

    public void Execute(object state = null)
    {
        try
        {
            Logger.LogInformation("Begin execute service");
            ExecuteAsync().Wait();
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Execute exception");
        }
        finally
        {
            Logger.LogInformation("Execute finished");
        }
    }

    protected abstract Task ExecuteAsync();

    public virtual void Dispose()
    {
        _timer?.Dispose();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Logger.LogInformation("Service is starting.");
        _timer.Change(TimeSpan.FromSeconds(SecurityHelper.Random.Next(10)), _period);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        Logger.LogInformation("Service is stopping.");

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }
}

基於上面這個基於Timer實現的后台定時任務類實現一個定時任務:

public class RemoveOverdueReservationService : ScheduledService
{
    public RemoveOverdueReservationService(ILogger<RemoveOverduedReservtaionService> logger) : base(TimeSpan.FromDays(1), logger)
    { }

    protected override Task ExecuteAsync()
    {
        return DependencyResolver.Current.TryInvokeServiceAsync<IEFRepository<ReservationDbContext, Reservation>>(reservationRepo =>
        {
            return reservationRepo.DeleteAsync(reservation => reservation.ReservationStatus == 0 && (reservation.ReservationForDate < DateTime.Today.AddDays(-3)));
        });
    }
}

這個類實現的是每天執行一次,刪除三天前狀態為待審核的預約,完整實現代碼:https://github.com/WeihanLi/ActivityReservation/blob/dev/ActivityReservation.Helper/Services/RemoveOverduedReservtaionService.cs

在程序啟動的時候注冊服務:

services.AddHostedService<RemoveOverduedReservtaionService>();

執行日志:

IHostedServiceBasedScheduledService Logs

通過日志可以看到我們的定時任務確實是每天執行一次,這樣我們的定時任務就算是簡單的完成了。

Reference


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM