From b8681c56f605cd640eddca3c0a013987243a7819 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Wed, 12 Jan 2022 14:48:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(tasks):=20=E5=A2=9E=E5=8A=A0=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=9F=BA=E4=BA=8E=E5=86=85=E5=AD=98=E5=AD=97=E5=85=B8?= =?UTF-8?q?=E7=9A=84=E4=BD=9C=E4=B8=9A=E9=94=81=E5=AE=9A=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BackgroundTasks/DefaultJobLockProvider.cs | 39 +++++++++++++++++++ .../Abp/BackgroundTasks/Jobs/ConsoleJob.cs | 2 +- .../Abp/BackgroundTasks/Jobs/SleepJob.cs | 2 +- .../Quartz/IQuartzJobExecutorProvider.cs | 2 +- .../Quartz/QuartzJobScheduler.cs | 3 +- .../Internal/JobExecutedEvent.cs | 12 +++++- .../Localization/Resources/en.json | 4 +- .../Localization/Resources/zh-Hans.json | 2 +- .../TaskManagement/BackgroundJobManager.cs | 2 + .../EfCoreBackgroundJobInfoRepository.cs | 2 + 10 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobLockProvider.cs diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobLockProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobLockProvider.cs new file mode 100644 index 000000000..0eaa28718 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/DefaultJobLockProvider.cs @@ -0,0 +1,39 @@ +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.BackgroundTasks; + +[Dependency(TryRegister = true)] +public class DefaultJobLockProvider : IJobLockProvider, ISingletonDependency +{ + private readonly ConcurrentDictionary _localSyncObjects = new(); + + public virtual Task TryLockAsync(string jobKey, int lockSeconds, CancellationToken cancellationToken = default) + { + Check.NotNullOrWhiteSpace(jobKey, nameof(jobKey)); + if (_localSyncObjects.ContainsKey(jobKey)) + { + return Task.FromResult(false); + } + + var semaphore = new SemaphoreSlim(1, 1); + return Task.FromResult(_localSyncObjects.TryAdd(jobKey, semaphore)); + } + + public Task TryReleaseAsync(string jobKey, CancellationToken cancellationToken = default) + { + Check.NotNullOrWhiteSpace(jobKey, nameof(jobKey)); + + if (_localSyncObjects.TryGetValue(jobKey, out var semaphore)) + { + semaphore.Dispose(); + + return Task.FromResult(_localSyncObjects.TryRemove(jobKey, out _)); + } + + return Task.FromResult(false); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs index abc1d747f..76fbf198e 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs @@ -9,7 +9,7 @@ public class ConsoleJob : IJobRunnable public Task ExecuteAsync(JobRunnableContext context) { context.TryGetString(PropertyMessage, out var message); - Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}] - This message: {message ?? "None"} comes from the job: {GetType()}"); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] - This message: {message ?? "None"} comes from the job: {GetType()}"); return Task.CompletedTask; } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob.cs index 9ec6be494..ca78365f3 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SleepJob.cs @@ -9,7 +9,7 @@ public class SleepJob : IJobRunnable { context.JobData.TryGetValue("Delay", out var sleep); - Console.WriteLine($"Sleep {sleep ?? 20000} milliseconds."); + Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] - Sleep {sleep ?? 20000} milliseconds."); await Task.Delay(sleep?.To() ?? 20000); } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs index 6fd447334..0bd0b9b76 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/IQuartzJobExecutorProvider.cs @@ -7,6 +7,6 @@ public interface IQuartzJobExecutorProvider #nullable enable IJobDetail? CreateJob(JobInfo job); - ITrigger CreateTrigger(JobInfo job); + ITrigger? CreateTrigger(JobInfo job); #nullable disable } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs index d6d5dc5d0..cdea17f83 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs @@ -1,7 +1,6 @@ using Quartz; using System.Collections.Generic; using System.Threading.Tasks; -using Volo.Abp; using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.BackgroundTasks.Quartz; @@ -84,7 +83,7 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency continue; } - jobDictionary.Add(jobDetail, new ITrigger[] { jobTrigger }); + jobDictionary[jobDetail] = new ITrigger[] { jobTrigger }; } await Scheduler.ScheduleJobs(jobDictionary, false); diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs index 8fd996c10..5c9e37e22 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs @@ -34,7 +34,7 @@ public class JobExecutedEvent : JobEventBase, ITransientDepend job.IsAbandoned = false; // 将任务标记为运行中, 会被轮询重新进入队列 job.Status = JobStatus.FailedRetry; - job.Result = context.EventData.Exception.Message; + job.Result = GetExceptionMessage(context.EventData.Exception); // 多次异常后需要重新计算优先级 if (job.TryCount <= (job.MaxTryCount / 2) && @@ -93,4 +93,14 @@ public class JobExecutedEvent : JobEventBase, ITransientDepend Logger.LogWarning($"An exception thow with job exception notify: {ex.Message}"); } } + + private string GetExceptionMessage(Exception exception) + { + if (exception.InnerException != null) + { + return exception.Message + " => " + GetExceptionMessage(exception.InnerException); + } + + return exception.Message; + } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json index d5511fbd1..6682b41c2 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json @@ -39,8 +39,8 @@ "Description:IsAbandoned": "If a job fails for several times and reaches the maximum number of retries, the job is marked as no longer being executed. You can manually add the job to the queue by changing the maximum number of retries.", "DisplayName:CreationTime": "CreationTime", "DisplayName:Result": "Last Execution Result", - "DisplayName:LastRunTime": "LastRunTime", - "DisplayName:NextRunTime": "NextRunTime", + "DisplayName:LastRunTime": "Last execution time", + "DisplayName:NextRunTime": "Next expected time", "DisplayName:Cron": "Cron expression", "Description:Cron": "Category is valid when periodic job, reference: https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/crontrigger.html#introduction", "DisplayName:MaxCount": "Max Count", diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json index 3c8362960..ea2bd8e16 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json @@ -40,7 +40,7 @@ "DisplayName:CreationTime": "创建时间", "DisplayName:Result": "上次执行结果", "DisplayName:LastRunTime": "上次执行时间", - "DisplayName:NextRunTime": "下次执行时间", + "DisplayName:NextRunTime": "下次预期时间", "DisplayName:Cron": "Cron表达式", "Description:Cron": "类别为周期性作业时有效, 参考: https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/crontrigger.html#introduction", "DisplayName:MaxCount": "最大触发次数", diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs index fcede54a9..b65ffeda8 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs @@ -116,6 +116,8 @@ public class BackgroundJobManager : DomainService await JobScheduler.ResumeAsync(job); jobInfo.SetStatus(JobStatus.Running); + jobInfo.IsAbandoned = false; + jobInfo.IsEnabled = true; await BackgroundJobInfoRepository.UpdateAsync(jobInfo); } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs index 968a0aff1..7cb6d11e3 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs @@ -58,6 +58,7 @@ public class EfCoreBackgroundJobInfoRepository : return await (await GetDbSetAsync()) .Where(x => x.IsEnabled && !x.IsAbandoned) .Where(x => x.JobType == JobType.Period && status.Contains(x.Status)) + .Where(x => (x.MaxCount == 0 || x.TriggerCount < x.MaxCount) || (x.MaxTryCount == 0 || x.TryCount < x.MaxTryCount)) .OrderByDescending(x => x.Priority) .ToListAsync(GetCancellationToken(cancellationToken)); } @@ -114,6 +115,7 @@ public class EfCoreBackgroundJobInfoRepository : return await (await GetDbSetAsync()) .Where(x => x.IsEnabled && !x.IsAbandoned) .Where(x => x.JobType != JobType.Period && status.Contains(x.Status)) + .Where(x => (x.MaxCount == 0 || x.TriggerCount < x.MaxCount) || (x.MaxTryCount == 0 || x.TryCount < x.MaxTryCount)) .OrderByDescending(x => x.Priority) .ThenBy(x => x.TryCount) .ThenBy(x => x.NextRunTime)