diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs index 2f808981f..8f69bde53 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksOptions.cs @@ -20,6 +20,16 @@ public class AbpBackgroundTasksOptions /// public ITypeList DefinitionProviders { get; } /// + /// 作业发布预配置 + /// + /// + /// Tips:
+ /// 仅作用于适用于 IBackgroundJobManager与IBackgroundWorker 适配器接口的作业预配置 + ///
+ /// 标准实现的作业参数请通过接口直接管理 + ///
+ public IJobDispatcherSelectorList JobDispatcherSelectors { get; } + /// /// 启用清理任务 /// /// @@ -93,5 +103,6 @@ public class AbpBackgroundTasksOptions JobMonitors = new TypeList(); DefinitionProviders = new TypeList(); + JobDispatcherSelectors = new JobDispatcherSelectorList(); } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobDispatcherSelectorList.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobDispatcherSelectorList.cs new file mode 100644 index 000000000..4a836f2be --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobDispatcherSelectorList.cs @@ -0,0 +1,8 @@ +using System.Collections; +using System.Collections.Generic; + +namespace LINGYUN.Abp.BackgroundTasks; +public interface IJobDispatcherSelectorList : IList, ICollection, IEnumerable, IEnumerable +{ + +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorList.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorList.cs new file mode 100644 index 000000000..15e693c4f --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorList.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.BackgroundTasks; +public class JobDispatcherSelectorList : List, IJobDispatcherSelectorList +{ + +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorListExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorListExtensions.cs new file mode 100644 index 000000000..d78d68a26 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorListExtensions.cs @@ -0,0 +1,122 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp; + +namespace LINGYUN.Abp.BackgroundTasks; +public static class JobDispatcherSelectorListExtensions +{ + public const string AllJobssSelectorName = "All"; + + public static void AddNamespace( + [NotNull] this IJobDispatcherSelectorList selectors, + [NotNull] string namespaceName, + [CanBeNull] Action setup = null) + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Namespace:" + namespaceName; + if (selectors.Any(s => s.Name == selectorName)) + { + return; + } + + var selector = new JobTypeSelector(selectorName, t => t.FullName?.StartsWith(namespaceName) ?? false); + + setup?.Invoke(selector); + + selectors.Add(selector); + } + + public static void Add([NotNull] this IJobDispatcherSelectorList selectors, [CanBeNull] Action setup = null) + where TJob : IJobRunnable + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Job:" + typeof(TJob).FullName; + if (selectors.Any(s => s.Name == selectorName)) + { + return; + } + + var selector = new JobTypeSelector(selectorName, t => typeof(TJob).IsAssignableFrom(t)); + + setup?.Invoke(selector); + + selectors.Add(selector); + } + + public static void Remove([NotNull] this IJobDispatcherSelectorList selectors) + where TJob : IJobRunnable + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Job:" + typeof(TJob).FullName; + selectors.RemoveAll(s => s.Name == selectorName); + } + + public static void AddAll([NotNull] this IJobDispatcherSelectorList selectors, [CanBeNull] Action setup = null) + { + Check.NotNull(selectors, nameof(selectors)); + + if (selectors.Any(s => s.Name == AllJobssSelectorName)) + { + return; + } + + var selector = new JobTypeSelector(AllJobssSelectorName, t => typeof(IJobRunnable).IsAssignableFrom(t)); + + setup?.Invoke(selector); + + selectors.Add(selector); + } + + public static void Add( + [NotNull] this IJobDispatcherSelectorList selectors, + string selectorName, + Func predicate, + [CanBeNull] Action setup = null) + { + Check.NotNull(selectors, nameof(selectors)); + + if (selectors.Any(s => s.Name == selectorName)) + { + throw new AbpException($"There is already a selector added before with the same name: {selectorName}"); + } + + var selector = new JobTypeSelector(selectorName, predicate); + + setup?.Invoke(selector); + + selectors.Add(selector); + } + + public static void Add( + [NotNull] this IJobDispatcherSelectorList selectors, + Func predicate, + [CanBeNull] Action setup = null) + { + var selector = new JobTypeSelector(Guid.NewGuid().ToString("N"), predicate); + + setup?.Invoke(selector); + + selectors.Add(selector); + } + + public static bool RemoveByName( + [NotNull] this IJobDispatcherSelectorList selectors, + [NotNull] string name) + { + Check.NotNull(selectors, nameof(selectors)); + Check.NotNull(name, nameof(name)); + + return selectors.RemoveAll(s => s.Name == name).Count > 0; + } + + public static bool IsMatch([NotNull] this IJobDispatcherSelectorList selectors, Type jobType) + { + Check.NotNull(selectors, nameof(selectors)); + return selectors.Any(s => s.Predicate(jobType)); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobTypeSelector.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobTypeSelector.cs new file mode 100644 index 000000000..306456aab --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobTypeSelector.cs @@ -0,0 +1,61 @@ +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.BackgroundTasks; +public class JobTypeSelector : NamedTypeSelector +{ + public JobTypeSelector( + string name, + Func predicate, + int lockTimeOut = 0, + string nodeName = null, + string cron = null, + JobPriority priority = JobPriority.Normal, + int interval = 300, + int tryCount = 0, + int maxTryCount = 50) + : base(name, predicate) + { + LockTimeOut = lockTimeOut; + NodeName = nodeName; + Cron = cron; + Priority = priority; + Interval = interval; + TryCount = tryCount; + MaxTryCount = maxTryCount; + } + + + + /// + /// 任务独占超时时长(秒) + /// 0或更小不生效 + /// + public int LockTimeOut { get; set; } + /// + /// 指定运行节点 + /// + public string NodeName { get; set; } + /// + /// 任务优先级 + /// + public JobPriority Priority { get; set; } = JobPriority.Normal; + /// + /// Cron表达式,如果是周期性任务需要指定 + /// + public string Cron { get; set; } + /// + /// 间隔时间,单位秒,与Cron表达式冲突 + /// 默认: 300 + /// + public int Interval { get; set; } = 300; + /// + /// 失败重试次数 + /// + public int TryCount { get; set; } + /// + /// 失败重试上限 + /// 默认:50 + /// + public int MaxTryCount { get; set; } = 50; +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs index 8c79f3eac..0acb82aa2 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Options; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; @@ -81,6 +82,29 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency Type = typeof(BackgroundJobAdapter).AssemblyQualifiedName, }; + if (TasksOptions.JobDispatcherSelectors.IsMatch(jobConfiguration.JobType)) + { + var selector = TasksOptions + .JobDispatcherSelectors + .FirstOrDefault(x => x.Predicate(jobConfiguration.JobType)); + + jobInfo.Interval = selector.Interval; + jobInfo.LockTimeOut = selector.LockTimeOut; + jobInfo.Priority = selector.Priority; + jobInfo.TryCount = selector.TryCount; + jobInfo.MaxTryCount = selector.MaxTryCount; + + if (!selector.NodeName.IsNullOrWhiteSpace()) + { + jobInfo.NodeName = selector.NodeName; + } + + if (!selector.Cron.IsNullOrWhiteSpace()) + { + jobInfo.Cron = selector.Cron; + } + } + // 存储状态 await JobStore.StoreAsync(jobInfo); diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerManager.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerManager.cs index 5c472c368..c0e6a538e 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerManager.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundWorkerManager.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Options; using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Volo.Abp.BackgroundWorkers; @@ -18,19 +19,22 @@ public class BackgroundWorkerManager : IBackgroundWorkerManager, ISingletonDepen protected IJobPublisher JobPublisher { get; } protected ICurrentTenant CurrentTenant { get; } protected AbpBackgroundTasksOptions Options { get; } + protected AbpBackgroundTasksOptions TasksOptions { get; } public BackgroundWorkerManager( IClock clock, IJobStore jobStore, IJobPublisher jobPublisher, ICurrentTenant currentTenant, - IOptions options) + IOptions options, + IOptions taskOptions) { Clock = clock; JobStore = jobStore; JobPublisher = jobPublisher; CurrentTenant = currentTenant; Options = options.Value; + TasksOptions = taskOptions.Value; } public async Task AddAsync(IBackgroundWorker worker, CancellationToken cancellationToken = default) @@ -50,6 +54,31 @@ public class BackgroundWorkerManager : IBackgroundWorkerManager, ISingletonDepen jobInfo.BeginTime = Clock.Now; jobInfo.CreationTime = Clock.Now; jobInfo.TenantId = CurrentTenant.Id; + + var workerType = ProxyHelper.GetUnProxiedType(worker); + if (workerType != null && TasksOptions.JobDispatcherSelectors.IsMatch(workerType)) + { + var selector = TasksOptions + .JobDispatcherSelectors + .FirstOrDefault(x => x.Predicate(workerType)); + + jobInfo.Interval = selector.Interval; + jobInfo.LockTimeOut = selector.LockTimeOut; + jobInfo.Priority = selector.Priority; + jobInfo.TryCount = selector.TryCount; + jobInfo.MaxTryCount = selector.MaxTryCount; + + if (!selector.NodeName.IsNullOrWhiteSpace()) + { + jobInfo.NodeName = selector.NodeName; + } + + if (!selector.Cron.IsNullOrWhiteSpace()) + { + jobInfo.Cron = selector.Cron; + } + } + // 存储状态 await JobStore.StoreAsync(jobInfo, cancellationToken); diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorListExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorListExtensions.cs new file mode 100644 index 000000000..9beffaa4b --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobDispatcherSelectorListExtensions.cs @@ -0,0 +1,81 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.BackgroundWorkers; + +namespace LINGYUN.Abp.BackgroundTasks; +public static class JobDispatcherSelectorListExtensions +{ + /// + /// + /// + /// + /// + /// + /// + /// Tips: 仅作用于适用于 接口的作业预配置 + /// + public static void AddJob([NotNull] this IJobDispatcherSelectorList selectors, [CanBeNull] Action setup = null) + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Job:" + typeof(TJob).FullName; + if (selectors.Any(s => s.Name == selectorName)) + { + return; + } + + var selector = new JobTypeSelector(selectorName, t => typeof(TJob).IsAssignableFrom(t)); + + setup?.Invoke(selector); + + selectors.Add(selector); + } + + public static void RemoveJob([NotNull] this IJobDispatcherSelectorList selectors) + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Job:" + typeof(TJob).FullName; + selectors.RemoveAll(s => s.Name == selectorName); + } + + /// + /// + /// + /// + /// + /// + /// + /// Tips: 仅作用于适用于 接口的作业预配置 + /// + public static void AddWorker([NotNull] this IJobDispatcherSelectorList selectors, [CanBeNull] Action setup = null) + where TWorker : IBackgroundWorker + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Worker:" + typeof(TWorker).FullName; + if (selectors.Any(s => s.Name == selectorName)) + { + return; + } + + var selector = new JobTypeSelector(selectorName, t => typeof(TWorker).IsAssignableFrom(t)); + + setup?.Invoke(selector); + + selectors.Add(selector); + } + + public static void RemoveWorker([NotNull] this IJobDispatcherSelectorList selectors) + where TWorker : IBackgroundWorker + { + Check.NotNull(selectors, nameof(selectors)); + + var selectorName = "Worker:" + typeof(TWorker).FullName; + selectors.RemoveAll(s => s.Name == selectorName); + } +} diff --git a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs index 9f4601213..1f61f7df6 100644 --- a/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs +++ b/aspnet-core/services/LY.MicroService.WebhooksManagement.HttpApi.Host/WebhooksManagementHttpApiHostModule.Configure.cs @@ -5,6 +5,7 @@ using LINGYUN.Abp.ExceptionHandling.Emailing; using LINGYUN.Abp.Serilog.Enrichers.Application; using LINGYUN.Abp.Serilog.Enrichers.UniqueId; using LINGYUN.Abp.Webhooks; +using LINGYUN.Abp.Webhooks.BackgroundJobs; using LINGYUN.Abp.WebhooksManagement; using LINGYUN.Abp.Wrapper; using Medallion.Threading; @@ -130,10 +131,18 @@ public partial class WebhooksManagementHttpApiHostModule Configure(options => { options.NodeName = ApplicationName; + + options.JobDispatcherSelectors.AddJob( + job => + { + // 让用户自行决定重试次数, 作业管理器限定只执行一次 + job.TryCount = 1; + }); //options.JobDispatcherSelectors.AddNamespace( // "LINGYUN.Abp.Webhooks.BackgroundJobs", // job => // { + // // more // }); }); }