Browse Source

Merge pull request #461 from colinin/task-management-error-push

Task management error push
pull/474/head
yx lin 4 years ago
committed by GitHub
parent
commit
4e9f13b932
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobLockProvider.cs
  2. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs
  3. 10
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN.Abp.BackgroundTasks.ExceptionHandling.csproj
  4. 16
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs
  5. 109
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/JobExceptionNotifier.cs
  6. 15
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/en.json
  7. 15
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/zh-Hans.json
  8. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/ConsoleJob.cs
  9. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs
  10. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/AbpBackgroundTasksQuartzModule.cs
  11. 10
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs
  12. 22
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs
  13. 7
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs
  14. 56
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzTriggerListener.cs
  15. 1
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj
  16. 16
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs
  17. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs
  18. 61
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobLockProvider.cs
  19. 30
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs
  20. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/BackgroundTasksResource.cs
  21. 5
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/en.json
  22. 5
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/zh-Hans.json
  23. 6
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs
  24. 9
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs
  25. 1
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj
  26. 2
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs

19
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/IJobLockProvider.cs

@ -0,0 +1,19 @@
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks;
/// <summary>
/// 作业锁定提供者
/// </summary>
public interface IJobLockProvider
{
Task<bool> TryLockAsync(
string jobKey,
int lockSeconds,
CancellationToken cancellationToken = default);
Task<bool> TryReleaseAsync(
string jobKey,
CancellationToken cancellationToken = default);
}

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobRunnableContextExtensions.cs

@ -71,7 +71,7 @@ public static class JobRunnableContextExtensions
{ {
return value; return value;
} }
throw new ArgumentException($"Job required data {key} not specified."); throw new ArgumentException($"Job required data [{key}] not specified.");
} }
public static bool TryGetJobData(this JobRunnableContext context, string key, out object value) public static bool TryGetJobData(this JobRunnableContext context, string key, out object value)

10
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN.Abp.BackgroundTasks.ExceptionHandling.csproj

@ -8,6 +8,16 @@
<RootNamespace /> <RootNamespace />
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Localization\Resources\*.json" />
<None Remove="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Templates\*.tpl" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Localization\Resources\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\BackgroundTasks\ExceptionHandling\Templates\*.tpl" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Volo.Abp.Timing" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.Timing" Version="$(VoloAbpPackageVersion)" />
</ItemGroup> </ItemGroup>

16
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/AbpBackgroundTasksExceptionHandlingModule.cs

@ -1,5 +1,8 @@
using LINGYUN.Abp.BackgroundTasks.Jobs; using LINGYUN.Abp.BackgroundTasks.Jobs;
using LINGYUN.Abp.BackgroundTasks.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
@ -7,5 +10,18 @@ namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
[DependsOn(typeof(AbpBackgroundTasksJobsModule))] [DependsOn(typeof(AbpBackgroundTasksJobsModule))]
public class AbpBackgroundTasksExceptionHandlingModule : AbpModule public class AbpBackgroundTasksExceptionHandlingModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpBackgroundTasksExceptionHandlingModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<BackgroundTasksResource>()
.AddVirtualJson("/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources");
});
}
} }

109
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/JobExceptionNotifier.cs

@ -1,10 +1,16 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using LINGYUN.Abp.BackgroundTasks.Jobs; using LINGYUN.Abp.BackgroundTasks.Jobs;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TextTemplating;
using Volo.Abp.Timing; using Volo.Abp.Timing;
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
@ -13,64 +19,89 @@ namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling;
public class JobExceptionNotifier : IJobExceptionNotifier, ITransientDependency public class JobExceptionNotifier : IJobExceptionNotifier, ITransientDependency
{ {
public const string Prefix = "exception."; public const string Prefix = "exception.";
public const string JobGroup = "ExceptionNotifier";
public ILogger<JobExceptionNotifier> Logger { protected get; set; }
protected IClock Clock { get; } protected IClock Clock { get; }
protected IJobStore JobStore { get; } protected IEmailSender EmailSender { get; }
protected IJobScheduler JobScheduler { get; } protected ITemplateRenderer TemplateRenderer { get; }
public JobExceptionNotifier( public JobExceptionNotifier(
IClock clock, IClock clock,
IJobStore jobStore, IEmailSender emailSender,
IJobScheduler jobScheduler) ITemplateRenderer templateRenderer)
{ {
Clock = clock; Clock = clock;
JobStore = jobStore; EmailSender = emailSender;
JobScheduler = jobScheduler; TemplateRenderer = templateRenderer;
Logger = NullLogger<JobExceptionNotifier>.Instance;
} }
public virtual async Task NotifyAsync([NotNull] JobExceptionNotificationContext context) public virtual async Task NotifyAsync([NotNull] JobExceptionNotificationContext context)
{ {
// 异常所属分组不处理, 防止死循环
if (string.Equals(context.JobInfo.Group, JobGroup))
{
Logger.LogWarning($"There is a problem executing the job, reason: {context.Exception.Message}");
return;
}
var notifyKey = Prefix + SendEmailJob.PropertyTo; var notifyKey = Prefix + SendEmailJob.PropertyTo;
if (context.JobInfo.Args.TryGetValue(notifyKey, out var exceptionTo)) if (context.JobInfo.Args.TryGetValue(notifyKey, out var exceptionTo) &&
exceptionTo is string to)
{ {
var template = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyTemplate) ?? ""; var template = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyTemplate)?.ToString() ?? "";
var subject = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertySubject) ?? "From job execute exception"; var content = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyBody)?.ToString() ?? "";
var globalContext = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyContext) ?? "{}"; var subject = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertySubject)?.ToString() ?? "From job execute exception";
var from = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyFrom) ?? ""; var from = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyFrom)?.ToString() ?? "";
var culture = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyCulture) ?? CultureInfo.CurrentCulture.Name; var errorMessage = context.Exception.GetBaseException().Message;
var jobId = Guid.NewGuid(); if (template.IsNullOrWhiteSpace())
var jobArgs = new Dictionary<string, object>
{ {
{ SendEmailJob.PropertyTo, exceptionTo.ToString() }, await EmailSender.SendAsync(from, to, subject, content, false);
{ SendEmailJob.PropertySubject, subject }, return;
{ SendEmailJob.PropertyBody, context.Exception.GetBaseException().Message }, }
{ SendEmailJob.PropertyTemplate, template },
{ SendEmailJob.PropertyContext, globalContext }, var footer = context.JobInfo.Args.GetOrDefault("footer")?.ToString() ?? $"Copyright to LY Colin © {Clock.Now.Year}";
{ SendEmailJob.PropertyFrom, from }, var model = new
{ SendEmailJob.PropertyCulture, culture }
};
var jobInfo = new JobInfo
{ {
Id = jobId, Title = subject,
Name = jobId.ToString(), Group = context.JobInfo.Group,
Group = "ExceptionHandling", Name = context.JobInfo.Name,
Priority = JobPriority.Normal, Id = context.JobInfo.Id,
BeginTime = Clock.Now, Type = context.JobInfo.Type,
Args = jobArgs, Triggertime = Clock.Now.ToString("yyyy-MM-dd HH:mm:ss"),
Description = subject.ToString(), Message = errorMessage,
JobType = JobType.Once, Tenantname = context.JobInfo.Args.GetOrDefault(nameof(IMultiTenant.TenantId)),
Interval = 5, Footer = footer,
MaxCount = 1,
MaxTryCount = 1,
CreationTime = Clock.Now,
Status = JobStatus.None,
Type = DefaultJobNames.SendEmailJob,
}; };
await JobStore.StoreAsync(jobInfo); var globalContext = new Dictionary<string, object>();
if (context.JobInfo.Args.TryGetValue(Prefix + SendEmailJob.PropertyContext, out var ctx) &&
ctx is string ctxStr && !ctxStr.IsNullOrWhiteSpace())
{
try
{
globalContext = JsonConvert.DeserializeObject<Dictionary<string, object>>(ctxStr);
}
catch { }
}
var culture = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyCulture)?.ToString() ?? CultureInfo.CurrentCulture.Name;
await JobScheduler.TriggerAsync(jobInfo); content = await TemplateRenderer.RenderAsync(
templateName: template,
model: model,
cultureName: culture,
globalContext: globalContext);
if (from.IsNullOrWhiteSpace())
{
await EmailSender.SendAsync(to, subject, content, true);
return;
}
await EmailSender.SendAsync(from, to, subject, content, true);
} }
} }
} }

15
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/en.json

@ -0,0 +1,15 @@
{
"culture": "en",
"texts": {
"TextTemplate:JobExceptionNotifier": "Background job exception pushes template",
"JobExceptionNotifier": "Background job exception push",
"TenantName": "TenantName",
"JobGroup": "Job Group",
"JobName": "Job Name",
"JobId": "Job Id",
"JobType": "Job Type",
"TriggerTime": "Trigger Time",
"ErrorMessage": "Error Message",
"JobExecuteError": "Background job exception"
}
}

15
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.ExceptionHandling/LINGYUN/Abp/BackgroundTasks/ExceptionHandling/Localization/Resources/zh-Hans.json

@ -0,0 +1,15 @@
{
"culture": "zh-Hans",
"texts": {
"TextTemplate:JobExceptionNotifier": "后台作业异常推送模板",
"JobExceptionNotifier": "后台作业异常推送",
"TenantName": "租户名称",
"JobGroup": "作业分组",
"JobName": "作业名称",
"JobId": "作业标识",
"JobType": "作业类型",
"TriggerTime": "触发时间",
"ErrorMessage": "错误消息",
"JobExecuteError": "后台作业异常"
}
}

2
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) public Task ExecuteAsync(JobRunnableContext context)
{ {
context.TryGetString(PropertyMessage, out var message); context.TryGetString(PropertyMessage, out var message);
Console.WriteLine($"This message: {message ?? "None"} comes from the job: {GetType()}"); Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}] - This message: {message ?? "None"} comes from the job: {GetType()}");
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

30
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Jobs/LINGYUN/Abp/BackgroundTasks/Jobs/SendEmailJob.cs

@ -4,6 +4,7 @@ using Volo.Abp.TextTemplating;
using Volo.Abp.Json; using Volo.Abp.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using Newtonsoft.Json;
namespace LINGYUN.Abp.BackgroundTasks.Jobs; namespace LINGYUN.Abp.BackgroundTasks.Jobs;
@ -28,6 +29,10 @@ public class SendEmailJob : IJobRunnable
/// </summary> /// </summary>
public const string PropertyTemplate = "template"; public const string PropertyTemplate = "template";
/// <summary> /// <summary>
/// 可选, 模板消息中的模型参数
/// </summary>
public const string PropertyModel = "model";
/// <summary>
/// 可选, 模板消息中的上下文参数 /// 可选, 模板消息中的上下文参数
/// </summary> /// </summary>
public const string PropertyContext = "context"; public const string PropertyContext = "context";
@ -58,19 +63,40 @@ public class SendEmailJob : IJobRunnable
catch { } catch { }
} }
object model = null;
if (context.TryGetString(PropertyModel, out var modelString) && !modelString.IsNullOrWhiteSpace())
{
try
{
model = JsonConvert.DeserializeObject(modelString);
}
catch { }
}
var templateRenderer = context.GetRequiredService<ITemplateRenderer>(); var templateRenderer = context.GetRequiredService<ITemplateRenderer>();
var content = await templateRenderer.RenderAsync( var content = await templateRenderer.RenderAsync(
templateName: template, templateName: template,
model: model,
cultureName: culture, cultureName: culture,
globalContext: globalContext); globalContext: globalContext);
await emailSender.QueueAsync(from, to, subject, content, true); await QueueEmail(emailSender, from, to, subject, content, true);
return; return;
} }
var body = context.GetString(PropertyBody); var body = context.GetString(PropertyBody);
await emailSender.QueueAsync(from, to, subject, body, false); await QueueEmail(emailSender, from, to, subject, body, false);
}
private async Task QueueEmail(IEmailSender emailSender, string from, string to, string subject, string body, bool isBodyHtml = true)
{
if (from.IsNullOrWhiteSpace())
{
await emailSender.SendAsync(to, subject, body, isBodyHtml);
return;
}
await emailSender.SendAsync(from, to, subject, body, isBodyHtml);
} }
} }

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/AbpBackgroundTasksQuartzModule.cs

@ -15,5 +15,7 @@ public class AbpBackgroundTasksQuartzModule : AbpModule
var _scheduler = context.ServiceProvider.GetRequiredService<IScheduler>(); var _scheduler = context.ServiceProvider.GetRequiredService<IScheduler>();
_scheduler.ListenerManager.AddJobListener(context.ServiceProvider.GetRequiredService<QuartzJobListener>()); _scheduler.ListenerManager.AddJobListener(context.ServiceProvider.GetRequiredService<QuartzJobListener>());
_scheduler.ListenerManager.AddTriggerListener(context.ServiceProvider.GetRequiredService<QuartzTriggerListener>());
} }
} }

10
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs

@ -36,10 +36,12 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD
} }
var adapterType = typeof(QuartzJobSimpleAdapter<>); var adapterType = typeof(QuartzJobSimpleAdapter<>);
if (job.LockTimeOut > 0)
{ // 注释, 通过触发器监听锁定
adapterType = typeof(QuartzJobConcurrentAdapter<>); //if (job.LockTimeOut > 0)
} //{
// adapterType = typeof(QuartzJobConcurrentAdapter<>);
//}
if (!typeof(IJob).IsAssignableFrom(jobType)) if (!typeof(IJob).IsAssignableFrom(jobType))
{ {

22
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobListener.cs

@ -4,11 +4,12 @@ using Microsoft.Extensions.Logging.Abstractions;
using Quartz; using Quartz;
using Quartz.Listener; using Quartz.Listener;
using System; using System;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow; using Volo.Abp.Timing;
namespace LINGYUN.Abp.BackgroundTasks.Quartz; namespace LINGYUN.Abp.BackgroundTasks.Quartz;
@ -18,13 +19,16 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
public override string Name => "QuartzJobListener"; public override string Name => "QuartzJobListener";
protected IClock Clock { get; }
protected IJobEventProvider EventProvider { get; } protected IJobEventProvider EventProvider { get; }
protected IServiceProvider ServiceProvider { get; } protected IServiceProvider ServiceProvider { get; }
public QuartzJobListener( public QuartzJobListener(
IClock clock,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IJobEventProvider eventProvider) IJobEventProvider eventProvider)
{ {
Clock = clock;
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
EventProvider = eventProvider; EventProvider = eventProvider;
@ -50,6 +54,12 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
{ {
try try
{ {
var jobEventList = EventProvider.GetAll();
if (!jobEventList.Any())
{
return;
}
using var scope = ServiceProvider.CreateScope(); using var scope = ServiceProvider.CreateScope();
var jobEventData = new JobEventData( var jobEventData = new JobEventData(
jobUUId, jobUUId,
@ -60,7 +70,6 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
Result = context.Result?.ToString() Result = context.Result?.ToString()
}; };
var jobEventList = EventProvider.GetAll();
var eventContext = new JobEventContext( var eventContext = new JobEventContext(
scope.ServiceProvider, scope.ServiceProvider,
jobEventData); jobEventData);
@ -86,6 +95,12 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
{ {
try try
{ {
var jobEventList = EventProvider.GetAll();
if (!jobEventList.Any())
{
return;
}
using var scope = ServiceProvider.CreateScope(); using var scope = ServiceProvider.CreateScope();
var jobId = context.GetString(nameof(JobInfo.Id)); var jobId = context.GetString(nameof(JobInfo.Id));
if (Guid.TryParse(jobId, out var jobUUId)) if (Guid.TryParse(jobId, out var jobUUId))
@ -112,7 +127,7 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
jobEventData.RepeatCount = simpleTrigger.RepeatCount; jobEventData.RepeatCount = simpleTrigger.RepeatCount;
} }
jobEventData.Description = context.JobDetail.Description; jobEventData.Description = context.JobDetail.Description;
jobEventData.RunTime = context.FireTimeUtc.LocalDateTime; jobEventData.RunTime = Clock.Now;
jobEventData.LastRunTime = context.PreviousFireTimeUtc?.LocalDateTime; jobEventData.LastRunTime = context.PreviousFireTimeUtc?.LocalDateTime;
jobEventData.NextRunTime = context.NextFireTimeUtc?.LocalDateTime; jobEventData.NextRunTime = context.NextFireTimeUtc?.LocalDateTime;
if (context.Result != null) if (context.Result != null)
@ -125,7 +140,6 @@ public class QuartzJobListener : JobListenerSupport, ISingletonDependency
jobEventData.TenantId = tenantId; jobEventData.TenantId = tenantId;
} }
var jobEventList = EventProvider.GetAll();
var eventContext = new JobEventContext( var eventContext = new JobEventContext(
scope.ServiceProvider, scope.ServiceProvider,
jobEventData); jobEventData);

7
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs

@ -154,10 +154,11 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
var jobKey = new JobKey(job.Name, job.Group); var jobKey = new JobKey(job.Name, job.Group);
if (!await Scheduler.CheckExists(jobKey)) if (!await Scheduler.CheckExists(jobKey))
{ {
job.JobType = JobType.Once;
await QueueAsync(job); await QueueAsync(job);
} }
await Scheduler.TriggerJob(jobKey); else
{
await Scheduler.TriggerJob(jobKey);
}
} }
} }

56
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzTriggerListener.cs

@ -0,0 +1,56 @@
using Quartz;
using Quartz.Listener;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundTasks.Quartz;
public class QuartzTriggerListener : TriggerListenerSupport, ISingletonDependency
{
protected const string LockKeyFormat = "p:abp-background-tasks,job:{0},key:{1}";
public override string Name => "QuartzTriggerListener";
protected IJobLockProvider JobLockProvider { get; }
public QuartzTriggerListener(
IJobLockProvider jobLockProvider)
{
JobLockProvider = jobLockProvider;
}
public override async Task<bool> VetoJobExecution(
ITrigger trigger,
IJobExecutionContext context,
CancellationToken cancellationToken = default)
{
context.MergedJobDataMap.TryGetValue(nameof(JobInfo.Id), out var jobId);
context.MergedJobDataMap.TryGetValue(nameof(JobInfo.LockTimeOut), out var lockTime);
if (jobId != null && lockTime != null && lockTime is int time && time > 0)
{
return !await JobLockProvider.TryLockAsync(NormalizeKey(context, jobId), time, cancellationToken);
}
return false;
}
public override async Task TriggerComplete(
ITrigger trigger,
IJobExecutionContext context,
SchedulerInstruction triggerInstructionCode,
CancellationToken cancellationToken = default)
{
if (context.MergedJobDataMap.TryGetValue(nameof(JobInfo.Id), out var jobId) &&
context.MergedJobDataMap.ContainsKey(nameof(JobInfo.LockTimeOut)))
{
await JobLockProvider.TryReleaseAsync(NormalizeKey(context, jobId), cancellationToken);
}
}
protected virtual string NormalizeKey(IJobExecutionContext context, object jobId)
{
return string.Format(LockKeyFormat, context.JobDetail.JobType.Name, jobId.ToString());
}
}

1
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN.Abp.BackgroundTasks.csproj

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Volo.Abp.Auditing" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.Auditing" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.BackgroundJobs.Abstractions" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Caching" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.DistributedLocking.Abstractions" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.DistributedLocking.Abstractions" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpPackageVersion)" />
</ItemGroup> </ItemGroup>

16
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/AbpBackgroundTasksModule.cs

@ -1,14 +1,18 @@
using LINGYUN.Abp.BackgroundTasks.Internal; using LINGYUN.Abp.BackgroundTasks.Internal;
using LINGYUN.Abp.BackgroundTasks.Localization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Auditing; using Volo.Abp.Auditing;
using Volo.Abp.BackgroundJobs; using Volo.Abp.BackgroundJobs;
using Volo.Abp.DistributedLocking; using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Localization;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.BackgroundTasks; namespace LINGYUN.Abp.BackgroundTasks;
[DependsOn(typeof(AbpAuditingModule))] [DependsOn(typeof(AbpAuditingModule))]
[DependsOn(typeof(AbpLocalizationModule))]
[DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))] [DependsOn(typeof(AbpBackgroundTasksAbstractionsModule))]
[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))] [DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))]
[DependsOn(typeof(AbpDistributedLockingAbstractionsModule))] [DependsOn(typeof(AbpDistributedLockingAbstractionsModule))]
@ -19,5 +23,17 @@ public class AbpBackgroundTasksModule : AbpModule
{ {
context.Services.AddTransient(typeof(BackgroundJobAdapter<>)); context.Services.AddTransient(typeof(BackgroundJobAdapter<>));
context.Services.AddHostedService<DefaultBackgroundWorker>(); context.Services.AddHostedService<DefaultBackgroundWorker>();
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpBackgroundTasksModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<BackgroundTasksResource>("en")
.AddVirtualJson("/LINGYUN/Abp/BackgroundTasks/Localization/Resources");
});
} }
} }

2
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs

@ -47,7 +47,7 @@ public class JobExecutedEvent : JobEventBase<JobExecutedEvent>, ITransientDepend
job.Priority = JobPriority.Low; job.Priority = JobPriority.Low;
} }
if (job.TryCount > job.MaxTryCount) if (job.TryCount >= job.MaxTryCount)
{ {
job.Status = JobStatus.Stopped; job.Status = JobStatus.Stopped;
job.IsAbandoned = true; job.IsAbandoned = true;

61
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobLockProvider.cs

@ -0,0 +1,61 @@
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
namespace LINGYUN.Abp.BackgroundTasks;
[Dependency(ReplaceServices = true)]
public class JobLockProvider : IJobLockProvider, ISingletonDependency
{
protected IMemoryCache LockCache { get; }
protected IAbpDistributedLock DistributedLock { get; }
public JobLockProvider(
IMemoryCache lockCache,
IAbpDistributedLock distributedLock)
{
LockCache = lockCache;
DistributedLock = distributedLock;
}
public virtual async Task<bool> TryLockAsync(string jobKey, int lockSeconds, CancellationToken cancellationToken = default)
{
var handle = await DistributedLock.TryAcquireAsync(jobKey, cancellationToken: cancellationToken);
if (handle != null)
{
await LockCache.GetOrCreateAsync(jobKey, (entry) =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(lockSeconds));
entry.RegisterPostEvictionCallback(async (key, value, reason, state) =>
{
if (reason == EvictionReason.Expired && value is IAbpDistributedLockHandle handleValue)
{
await handleValue.DisposeAsync();
}
});
entry.SetValue(handle);
return Task.FromResult(handle);
});
return true;
}
return false;
}
public virtual async Task<bool> TryReleaseAsync(string jobKey, CancellationToken cancellationToken = default)
{
if (LockCache.TryGetValue<IAbpDistributedLockHandle>(jobKey, out var handle))
{
await handle.DisposeAsync();
LockCache.Remove(jobKey);
return true;
}
return false;
}
}

30
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/JobRunnableExecuter.cs

@ -1,17 +1,13 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BackgroundTasks; namespace LINGYUN.Abp.BackgroundTasks;
public class JobRunnableExecuter : IJobRunnableExecuter, ISingletonDependency public class JobRunnableExecuter : IJobRunnableExecuter, ISingletonDependency
{ {
protected const string LockKeyFormat = "p:{0},job:{1},key:{2}";
public async virtual Task ExecuteAsync(JobRunnableContext context) public async virtual Task ExecuteAsync(JobRunnableContext context)
{ {
Guid? tenantId = null; Guid? tenantId = null;
@ -24,31 +20,7 @@ public class JobRunnableExecuter : IJobRunnableExecuter, ISingletonDependency
var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>(); var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();
using (currentTenant.Change(tenantId)) using (currentTenant.Change(tenantId))
{ {
context.JobData.TryGetValue(nameof(JobInfo.LockTimeOut), out var lockTime); await InternalExecuteAsync(context);
// 某些提供者如果无法保证锁一致性, 那么需要用分布式锁
if (lockTime != null && (lockTime is int time && time > 0))
{
var jobId = context.JobData.GetOrDefault(nameof(JobInfo.Id));
var jobLockKey = string.Format(LockKeyFormat, tenantId?.ToString() ?? "Default", context.JobType.Name, jobId);
var distributedLock = context.ServiceProvider.GetRequiredService<IAbpDistributedLock>();
var handle = await distributedLock.TryAcquireAsync(jobLockKey, TimeSpan.FromSeconds(time));
if (handle == null)
{
// 抛出异常 通过监听器使其重试
throw new AbpBackgroundTaskConcurrentException(context.JobType);
}
await using (handle)
{
await InternalExecuteAsync(context);
}
}
else
{
await InternalExecuteAsync(context);
}
} }
} }

8
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/BackgroundTasksResource.cs

@ -0,0 +1,8 @@
using Volo.Abp.Localization;
namespace LINGYUN.Abp.BackgroundTasks.Localization;
[LocalizationResourceName("BackgroundTasks")]
public class BackgroundTasksResource
{
}

5
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/en.json

@ -0,0 +1,5 @@
{
"culture": "en",
"texts": {
}
}

5
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Localization/Resources/zh-Hans.json

@ -0,0 +1,5 @@
{
"culture": "zh-Hans",
"texts": {
}
}

6
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs

@ -156,8 +156,10 @@ public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBa
backgroundJobInfo.MaxCount = input.MaxCount; backgroundJobInfo.MaxCount = input.MaxCount;
backgroundJobInfo.MaxTryCount = input.MaxTryCount; backgroundJobInfo.MaxTryCount = input.MaxTryCount;
backgroundJobInfo.Args.RemoveAll(x => !input.Args.ContainsKey(x.Key)); foreach (var arg in input.Args)
backgroundJobInfo.Args.AddIfNotContains(input.Args); {
backgroundJobInfo.Args[arg.Key] = arg.Value;
}
backgroundJobInfo.SetPriority(input.Priority); backgroundJobInfo.SetPriority(input.Priority);
switch (input.JobType) switch (input.JobType)

9
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs

@ -92,12 +92,9 @@ public class BackgroundJobManager : DomainService
public virtual async Task TriggerAsync(BackgroundJobInfo jobInfo) public virtual async Task TriggerAsync(BackgroundJobInfo jobInfo)
{ {
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo); var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
//if (!await JobScheduler.ExistsAsync(job)) job.JobType = JobType.Once;
//{ // 延迟两秒触发
// throw new BusinessException(TaskManagementErrorCodes.JobNotFoundInQueue) job.Interval = 2;
// .WithData("Group", job.Group)
// .WithData("Name", job.Name);
//}
await JobScheduler.TriggerAsync(job); await JobScheduler.TriggerAsync(job);
} }

1
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/LY.MicroService.TaskManagement.HttpApi.Host.csproj

@ -38,6 +38,7 @@
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Autofac" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.Autofac" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.DistributedLocking" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Swashbuckle" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.Swashbuckle" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.MySql" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.EntityFrameworkCore.MySql" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Http.Client.IdentityModel.Web" Version="$(VoloAbpPackageVersion)" /> <PackageReference Include="Volo.Abp.Http.Client.IdentityModel.Web" Version="$(VoloAbpPackageVersion)" />

2
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/TaskManagementHttpApiHostModule.cs

@ -22,6 +22,7 @@ using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac; using Volo.Abp.Autofac;
using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EntityFrameworkCore.MySQL; using Volo.Abp.EntityFrameworkCore.MySQL;
using Volo.Abp.FeatureManagement.EntityFrameworkCore; using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Http.Client.IdentityModel.Web; using Volo.Abp.Http.Client.IdentityModel.Web;
@ -38,6 +39,7 @@ namespace LY.MicroService.TaskManagement;
typeof(AbpSerilogEnrichersUniqueIdModule), typeof(AbpSerilogEnrichersUniqueIdModule),
typeof(AbpAuditLoggingElasticsearchModule), typeof(AbpAuditLoggingElasticsearchModule),
typeof(AbpAspNetCoreSerilogModule), typeof(AbpAspNetCoreSerilogModule),
typeof(AbpDistributedLockingModule),
typeof(AbpEntityFrameworkCoreMySQLModule), typeof(AbpEntityFrameworkCoreMySQLModule),
typeof(AbpAspNetCoreAuthenticationJwtBearerModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpEmailingExceptionHandlingModule), typeof(AbpEmailingExceptionHandlingModule),

Loading…
Cancel
Save