ASP.. NET Core 3.1托管服务为一个给定的时间表运行多次(它应该每天运行一次).&l



我在后台运行的asp.net core 3.1应用程序中托管了这个服务作业。在开发中,一切工作正常,但当我将其部署到生产中,从这个抽象类继承的任何作业都将多次运行ExecuteOnStartAsync()方法。在开发中测试DummyTestJob类时。它运行得很好,每10秒调用一次。然而,当我将其部署到生产环境中,并在凌晨2点发送1封电子邮件时,在am中我将有5-13封电子邮件,这个数字也是任意的。这是部署在一台AWS EC2 Windows机器上,我不确定它是否与cpu内核的数量等有关,但是我的本地开发工作站有8个内核,它仍然运行一次。所以我不确定还发生了什么。在使用asp.net core托管服务之前,您有过类似的经历吗?

public abstract class ScheduledBackgroundWorker<T> : BaseBackgroundWorker<T> where T : class
{
    private CrontabSchedule schedule;
    private DateTime nextRun;
    protected string JobName => this.GetType().Name;
    protected abstract string JobDescription { get; }
    protected abstract string Schedule { get; }
    protected ScheduledBackgroundWorker(IServiceProvider services,
       ILogger<T> logger) : base(services, logger)
    {
        schedule = CrontabSchedule.Parse(Schedule, new CrontabSchedule.ParseOptions
        {
            IncludingSeconds = true
        });
        nextRun = schedule.GetNextOccurrence(DateTime.Now);
    }
    protected abstract Task ExecuteOnStartAsync(CancellationToken cancellationToken);
    protected abstract Task ExecuteOnStopAsync(CancellationToken cancellationToken);
    protected override async Task ExecuteStartAsync(CancellationToken cancellationToken)
    {
        logger.LogInformation("{jobName} Schedule {schedule} was scheduled and next run will be at {nextRun} - {now}", JobName, Schedule, nextRun.ToString("MM/dd/yyyy hh:mm:ss t"), DateTime.Now);
        using var scope = services.CreateScope();
        var cache =
            scope.ServiceProvider
                .GetRequiredService<IDistributedCache>();
        do
        {
            var hasRun = await cache.GetAsync(JobName);
            if (hasRun == null)
            {
                var now = DateTime.Now;
                if (now > nextRun)
                {
                    logger.LogInformation("{jobName} Schedule {schedule} is running - {now}", JobName, Schedule, DateTime.Now);
                    await ExecuteOnStartAsync(cancellationToken);
                    nextRun = schedule.GetNextOccurrence(DateTime.Now);
                    var currentTime = nextRun.ToString();
                    byte[] encodedCurrentTime = Encoding.UTF8.GetBytes(currentTime);
                    var options = new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(60));
                    await cache.SetAsync(JobName, encodedCurrentTime, options, cancellationToken);
                    logger.LogInformation("{jobName} Schedule {schedule} has finished and next run will be at {nextRun} - {now}", JobName, Schedule, nextRun.ToString("MM/dd/yyyy hh:mm:ss t"), DateTime.Now);
                }
            }
            await Task.Delay(5000, cancellationToken); //5 seconds delay
        }
        while (!cancellationToken.IsCancellationRequested);
        await Task.CompletedTask;
    }
    protected override async Task ExecuteStopAsync(CancellationToken cancellationToken)
    {
        await ExecuteOnStopAsync(cancellationToken);
        logger.LogInformation("{jobName} Schedule {schedule} was stopped - {now}", JobName, Schedule, DateTime.Now);
        await Task.CompletedTask;
    }
    public override void Dispose()
    {
    }
}

即使在缓存中保存运行状态,它也会像没有缓存一样做同样的事情。我使用这个名为NCrontab的库来设置cron时间表。我不确定这和它有什么关系。但同样,它在开发中有效,在生产中失败。

public class DummyTestJob : ScheduledBackgroundWorker<DummyTestJob>
{
public DummyTestJob(IServiceProvider services,
    ILogger<DummyTestJob> logger) : base(services, logger)
{
}
protected override string JobDescription => "Dummy Test";
protected override string Schedule => "10 * * * * *";
protected override async Task ExecuteOnStartAsync(CancellationToken cancellationToken)
{
    using var scope = services.CreateScope();
    var apiClient =
        scope.ServiceProvider
            .GetRequiredService<IApiClient>();
    var runtimeEnvironment = scope.ServiceProvider.GetRequiredService<IRuntimeEnvironment>();
    try
    {
        var page = 1;
        var size = 100;
        var loop = true;
        List<User> users = new List<User>();
        do
        {
            try
            {
                var response = await apiClient.GetAsync($"users?page={page}&size={size}", null, null);
                if (response.Count > 0)
                {
                    users.AddRange(response.Data);
                    page++;
                }
                else
                {
                    loop = false;
                }
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Failure getting users for page {page}", page);
                loop = false;
            }
        } while (loop);
        List<string> userNames = new List<string>();
        foreach (var user in Users)
        {
            if (user.IsActive == 1)
            {
                userNames.Add(user.Name);
            }
        }
        users.Clear();
        users = null;
        foreach (var userName in userNames)
        {
            System.Console.WriteLine($"Hello {userName}...n");
        }
    }
    catch (OperationCanceledException ex)
    {
        logger.LogError(ex, "{JobName} execution canceled - {now}", JobName, DateTime.Now);
    }
    await Task.CompletedTask;
}
protected override async Task ExecuteOnStopAsync(CancellationToken cancellationToken)
{
    await Task.CompletedTask;
}
}

public abstract class BaseBackgroundWorker<T> : BackgroundService where T : class
{
    protected readonly IServiceProvider services;
    protected readonly ILogger<T> logger;
    // inject a logger
    protected BaseBackgroundWorker(IServiceProvider services,
        ILogger<T> logger)
    {
        this.services = services;
        this.logger = logger;
    }
    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        await ExecuteStartAsync(cancellationToken);
        await Task.CompletedTask;
    }
    protected abstract Task ExecuteStartAsync(CancellationToken cancellationToken);
    protected abstract Task ExecuteStopAsync(CancellationToken cancellationToken);
    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        await ExecuteStopAsync(cancellationToken);
        await base.StopAsync(cancellationToken);
        await Task.CompletedTask;
    }
}

这是因为在IIS中,我将API的最大工作进程设置为5。将其重置为1修复了这个问题

最新更新