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