11 changed files with 171 additions and 39 deletions
@ -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); |
|||
} |
|||
@ -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()); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue