From 26e51cbdbd805911474bd4d1349abb025ef82012 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 12 Oct 2021 18:46:26 +0800 Subject: [PATCH] Implement Hangfire Background Worker Manager --- framework/Volo.Abp.sln | 9 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++++++ ...Volo.Abp.BackgroundWorkers.Hangfire.csproj | 22 +++++ .../AbpBackgroundWorkersHangfireModule.cs | 36 ++++++++ .../Hangfire/HangfireBackgroundWorkerBase.cs | 11 +++ .../HangfireBackgroundWorkerManager.cs | 83 +++++++++++++++++++ ...HangfirePeriodicBackgroundWorkerAdapter.cs | 36 ++++++++ .../Hangfire/IHangfireBackgroundWorker.cs | 13 +++ nupkg/common.ps1 | 1 + 10 files changed, 244 insertions(+) create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xml create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xsd create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfirePeriodicBackgroundWorkerAdapter.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/IHangfireBackgroundWorker.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 309be85ea1..991d1d08d5 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -372,6 +372,7 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AzureServiceBus", "src\Volo.Abp.AzureServiceBus\Volo.Abp.AzureServiceBus.csproj", "{808EC18E-C8CC-4F5C-82B6-984EADBBF85D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Azure", "src\Volo.Abp.EventBus.Azure\Volo.Abp.EventBus.Azure.csproj", "{FB27F78E-F10E-4810-9B8E-BCD67DCFC8A2}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Authorization.Abstractions", "src\Volo.Abp.Authorization.Abstractions\Volo.Abp.Authorization.Abstractions.csproj", "{87B0C2A8-FE95-4779-8B9C-2181AA52B3FA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Core", "src\Volo.Abp.TextTemplating.Core\Volo.Abp.TextTemplating.Core.csproj", "{184E859A-282D-44D7-B8E9-FEA874644013}" @@ -393,10 +394,13 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Boxes", "src\Volo.Abp.EventBus.Boxes\Volo.Abp.EventBus.Boxes.csproj", "{6E289F31-7924-418B-9DAC-62A7CFADF916}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.DistributedLocking", "src\Volo.Abp.DistributedLocking\Volo.Abp.DistributedLocking.csproj", "{9A7EEA08-15BE-476D-8168-53039867038E}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Auditing.Contracts", "src\Volo.Abp.Auditing.Contracts\Volo.Abp.Auditing.Contracts.csproj", "{508B6355-AD28-4E60-8549-266D21DBF2CF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Http.Client.Web", "src\Volo.Abp.Http.Client.Web\Volo.Abp.Http.Client.Web.csproj", "{F7407459-8AFA-45E4-83E9-9BB01412CC08}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BackgroundWorkers.Hangfire", "src\Volo.Abp.BackgroundWorkers.Hangfire\Volo.Abp.BackgroundWorkers.Hangfire.csproj", "{E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1187,6 +1191,10 @@ Global {F7407459-8AFA-45E4-83E9-9BB01412CC08}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7407459-8AFA-45E4-83E9-9BB01412CC08}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7407459-8AFA-45E4-83E9-9BB01412CC08}.Release|Any CPU.Build.0 = Release|Any CPU + {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1388,6 +1396,7 @@ Global {9A7EEA08-15BE-476D-8168-53039867038E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {508B6355-AD28-4E60-8549-266D21DBF2CF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {F7407459-8AFA-45E4-83E9-9BB01412CC08} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {E5FCE710-C5A3-4F94-B9C9-BD1E99252BFB} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xml b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xml new file mode 100644 index 0000000000..bc5a74a236 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xsd b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj new file mode 100644 index 0000000000..fe87db9cd1 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo.Abp.BackgroundWorkers.Hangfire.csproj @@ -0,0 +1,22 @@ + + + + + + + netstandard2.0 + Volo.Abp.BackgroundWorkers.Hangfire + Volo.Abp.BackgroundWorkers.Hangfire + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs new file mode 100644 index 0000000000..4efbdcffb7 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/AbpBackgroundWorkersHangfireModule.cs @@ -0,0 +1,36 @@ +using System; +using Hangfire; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.Hangfire; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BackgroundWorkers.Hangfire +{ + [DependsOn( + typeof(AbpBackgroundWorkersModule), + typeof(AbpHangfireModule))] + public class AbpBackgroundWorkerHangfireModule : AbpModule + { + public override void OnPreApplicationInitialization(ApplicationInitializationContext context) + { + var options = context.ServiceProvider.GetRequiredService>().Value; + if (!options.IsEnabled) + { + var hangfireOptions = context.ServiceProvider.GetRequiredService>().Value; + hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer; + } + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddSingleton(typeof(HangfirePeriodicBackgroundWorkerAdapter<>)); + } + + private BackgroundJobServer CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider) + { + serviceProvider.GetRequiredService(); + return null; + } + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs new file mode 100644 index 0000000000..d5de154909 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerBase.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.BackgroundWorkers.Hangfire +{ + public abstract class HangfireBackgroundWorkerBase : BackgroundWorkerBase, IHangfireBackgroundWorker + { + public string CronExpression { get; set; } + + public abstract Task ExecuteAsync(); + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs new file mode 100644 index 0000000000..a1a3db2969 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs @@ -0,0 +1,83 @@ +using System; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Hangfire; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.BackgroundWorkers.Hangfire +{ + [Dependency(ReplaceServices = true)] + public class HangfireBackgroundWorkerManager : IBackgroundWorkerManager, ISingletonDependency + { + public Task StartAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return Task.CompletedTask; + } + + public void Add(IBackgroundWorker worker) + { + if (worker is IHangfireBackgroundWorker hangfireBackgroundWorker) + { + RecurringJob.AddOrUpdate(() => hangfireBackgroundWorker.ExecuteAsync(), + hangfireBackgroundWorker.CronExpression); + } + else + { + int? period; + + if (worker is AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase) + { + var timer = (AbpTimer) worker.GetType() + .GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker); + period = timer?.Period; + } + else + { + return; + } + + if (period == null) + { + return; + } + + var adapterType = typeof(HangfirePeriodicBackgroundWorkerAdapter<>).MakeGenericType(worker.GetType()); + var workerAdapter = Activator.CreateInstance(adapterType) as IHangfireBackgroundWorker; + + RecurringJob.AddOrUpdate(() => workerAdapter.ExecuteAsync(), GetCron(period.Value)); + } + } + + protected virtual string GetCron(int period) + { + var time = TimeSpan.FromMilliseconds(period); + string cron; + + if (time.TotalSeconds <= 59) + { + cron = $"*/{time.TotalSeconds} * * * * *"; + } + else if (time.TotalMinutes <= 59) + { + cron = $"*/{time.TotalMinutes} * * * *"; + } + else if (time.TotalHours <= 23) + { + cron = $"0 */{time.TotalHours} * * *"; + } + else + { + cron = $"0 0 */{time.TotalDays} * *"; + } + + return cron; + } + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfirePeriodicBackgroundWorkerAdapter.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfirePeriodicBackgroundWorkerAdapter.cs new file mode 100644 index 0000000000..c9e3b202eb --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfirePeriodicBackgroundWorkerAdapter.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.Abp.BackgroundWorkers.Hangfire +{ + public class HangfirePeriodicBackgroundWorkerAdapter : HangfireBackgroundWorkerBase + where TWorker : IBackgroundWorker + { + private readonly MethodInfo _doWorkAsyncMethod; + private readonly MethodInfo _doWorkMethod; + + public HangfirePeriodicBackgroundWorkerAdapter() + { + _doWorkAsyncMethod = + typeof(TWorker).GetMethod("DoWorkAsync", BindingFlags.Instance | BindingFlags.NonPublic); + _doWorkMethod = typeof(TWorker).GetMethod("DoWork", BindingFlags.Instance | BindingFlags.NonPublic); + } + + public override async Task ExecuteAsync() + { + var workerContext = new PeriodicBackgroundWorkerContext(ServiceProvider); + var worker = ServiceProvider.GetRequiredService(); + + switch (worker) + { + case AsyncPeriodicBackgroundWorkerBase asyncPeriodicBackgroundWorker: + await (Task) _doWorkAsyncMethod.Invoke(asyncPeriodicBackgroundWorker, new object[] {workerContext}); + break; + case PeriodicBackgroundWorkerBase periodicBackgroundWorker: + _doWorkMethod.Invoke(periodicBackgroundWorker, new object[] {workerContext}); + break; + } + } + } +} diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/IHangfireBackgroundWorker.cs b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/IHangfireBackgroundWorker.cs new file mode 100644 index 0000000000..19653a7091 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/IHangfireBackgroundWorker.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.BackgroundWorkers.Hangfire +{ + public interface IHangfireBackgroundWorker : IBackgroundWorker + { + string CronExpression { get; set; } + + Task ExecuteAsync(); + } +} + + diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 7d48c1ac1d..fd99c6a068 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -73,6 +73,7 @@ $projects = ( "framework/src/Volo.Abp.BackgroundJobs.Quartz", "framework/src/Volo.Abp.BackgroundWorkers", "framework/src/Volo.Abp.BackgroundWorkers.Quartz", + "framework/src/Volo.Abp.BackgroundWorkers.Hangfire", "framework/src/Volo.Abp.BlazoriseUI", "framework/src/Volo.Abp.BlobStoring", "framework/src/Volo.Abp.BlobStoring.FileSystem",