我在后台运行的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修复了这个问题