mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-04-19 21:02:49 +08:00
refactor: ♻️ 计划作业模块
This commit is contained in:
parent
e82a172598
commit
f50dfc8f19
@ -18,11 +18,13 @@ public sealed class DefaultEventPublisher : IEventPublisher
|
|||||||
_ = new TaskFactory<Task>().StartNew( //
|
_ = new TaskFactory<Task>().StartNew( //
|
||||||
async state => {
|
async state => {
|
||||||
var subscribers = (List<MethodInfo>)state;
|
var subscribers = (List<MethodInfo>)state;
|
||||||
await foreach (var msg in _eventChannel.Reader.ReadAllAsync()) {
|
await Parallel.ForEachAsync(_eventChannel.Reader.ReadAllAsync(), (msg, __) => {
|
||||||
_ = Parallel.ForEach( //
|
_ = Parallel.ForEach( //
|
||||||
subscribers.Where(x => x.GetParameters().FirstOrDefault()?.ParameterType == msg.GetType())
|
subscribers.Where(x => x.GetParameters().FirstOrDefault()?.ParameterType == msg.GetType())
|
||||||
, (x, _) => x.Invoke(App.GetService(x.DeclaringType), [msg]));
|
, (x, _) => x.Invoke(App.GetService(x.DeclaringType), [msg]));
|
||||||
}
|
return ValueTask.CompletedTask;
|
||||||
|
})
|
||||||
|
.ConfigureAwait(false);
|
||||||
}, App.EffectiveTypes.Where(x => typeof(IEventSubscriber).IsAssignableFrom(x) && x.IsClass && !x.IsAbstract).SelectMany(x => x.GetMethods(BindingFlags.Instance | BindingFlags.Public).Where(y => y.IsDefined(typeof(EventSubscribeAttribute)))).ToList());
|
}, App.EffectiveTypes.Where(x => typeof(IEventSubscriber).IsAssignableFrom(x) && x.IsClass && !x.IsAbstract).SelectMany(x => x.GetMethods(BindingFlags.Instance | BindingFlags.Public).Where(y => y.IsDefined(typeof(EventSubscribeAttribute)))).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NetAdmin.FreeSql.DbContext" Version="1.1.1" Label="refs"/>
|
<PackageReference Include="NetAdmin.FreeSql.DbContext" Version="1.1.1" Label="refs"/>
|
||||||
<PackageReference Include="NetAdmin.FreeSql.Provider.Sqlite" Version="1.1.1" Label="refs"/>
|
<PackageReference Include="NetAdmin.FreeSql.Provider.Sqlite" Version="1.1.1" Label="refs"/>
|
||||||
<PackageReference Include="Gurion" Version="1.2.9" Label="refs"/>
|
<PackageReference Include="Gurion" Version="1.2.10" Label="refs"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0"/>
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0"/>
|
||||||
<PackageReference Include="Minio" Version="6.0.4"/>
|
<PackageReference Include="Minio" Version="6.0.4"/>
|
||||||
<PackageReference Include="NSExt" Version="2.3.3"/>
|
<PackageReference Include="NSExt" Version="2.3.3"/>
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
namespace NetAdmin.Infrastructure.Schedule;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 作业处理程序
|
||||||
|
/// </summary>
|
||||||
|
public interface IJob
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 具体处理逻辑
|
||||||
|
/// </summary>
|
||||||
|
Task ExecuteAsync(CancellationToken cancelToken);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
namespace NetAdmin.Infrastructure.Schedule;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 作业配置
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public sealed class JobConfigAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 上一次执行时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? LastExecutionTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动时运行
|
||||||
|
/// </summary>
|
||||||
|
public bool RunOnStart { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器表达式
|
||||||
|
/// </summary>
|
||||||
|
public string TriggerCron { get; init; }
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
using Gurion.Schedule;
|
using Cronos;
|
||||||
using NetAdmin.Domain.Contexts;
|
using NetAdmin.Domain.Contexts;
|
||||||
using NetAdmin.Domain.Events;
|
using NetAdmin.Domain.Events;
|
||||||
using NetAdmin.Host.Filters;
|
using NetAdmin.Host.Filters;
|
||||||
using NetAdmin.SysComponent.Host.Jobs;
|
using NetAdmin.Host.Middlewares;
|
||||||
|
using NetAdmin.Infrastructure.Schedule;
|
||||||
using NetAdmin.SysComponent.Host.Utils;
|
using NetAdmin.SysComponent.Host.Utils;
|
||||||
using FreeSqlBuilder = NetAdmin.Infrastructure.Utils.FreeSqlBuilder;
|
using FreeSqlBuilder = NetAdmin.Infrastructure.Utils.FreeSqlBuilder;
|
||||||
|
|
||||||
@ -62,17 +63,58 @@ public static class ServiceCollectionExtensions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加定时任务
|
/// 添加定时任务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IServiceCollection AddSchedules(this IServiceCollection me, bool force = false, Action<ScheduleOptionsBuilder> optionsAction = null)
|
public static IServiceCollection AddSchedules(this IServiceCollection me, bool force = false)
|
||||||
{
|
{
|
||||||
return App.WebHostEnvironment.IsProduction() || force
|
if (!App.WebHostEnvironment.IsProduction() && !force) {
|
||||||
? me.AddSchedule( //
|
return me;
|
||||||
builder => {
|
}
|
||||||
_ = builder //
|
|
||||||
.AddJob<ScheduledJob>(true, Triggers.PeriodSeconds(1).SetRunOnStart(true))
|
|
||||||
.AddJob<FreeScheduledJob>(true, Triggers.PeriodMinutes(1).SetRunOnStart(true));
|
|
||||||
|
|
||||||
optionsAction?.Invoke(builder);
|
var jobTypes = App.EffectiveTypes
|
||||||
})
|
.Where(x => typeof(IJob).IsAssignableFrom(x) && x.IsClass && !x.IsAbstract && x.IsDefined(typeof(JobConfigAttribute)))
|
||||||
: me;
|
.ToDictionary(x => x, x => x.GetCustomAttribute<JobConfigAttribute>());
|
||||||
|
var runOnStartJobTypes = jobTypes.Where(x => //
|
||||||
|
x.Value.RunOnStart);
|
||||||
|
RunJob(runOnStartJobTypes);
|
||||||
|
_ = Task.Run(LoopTaskAsync);
|
||||||
|
return me;
|
||||||
|
|
||||||
|
#pragma warning disable S2190
|
||||||
|
async Task LoopTaskAsync()
|
||||||
|
#pragma warning restore S2190
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
await Task.Delay(1000).ConfigureAwait(false);
|
||||||
|
if (SafetyShopHostMiddleware.IsShutdown) {
|
||||||
|
Console.WriteLine(Ln.此节点已下线);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RunJob(jobTypes.Where(Filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filter(KeyValuePair<Type, JobConfigAttribute> x)
|
||||||
|
{
|
||||||
|
return !x.Value.TriggerCron.NullOrEmpty() &&
|
||||||
|
CronExpression.Parse(x.Value.TriggerCron, CronFormat.IncludeSeconds)
|
||||||
|
.GetNextOccurrence(x.Value.LastExecutionTime ?? DateTime.UtcNow.AddDays(-1), TimeZoneInfo.Local)
|
||||||
|
?.ToLocalTime() <= DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once FunctionNeverReturns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunJob(IEnumerable<KeyValuePair<Type, JobConfigAttribute>> jobTypes)
|
||||||
|
{
|
||||||
|
foreach (var job in jobTypes) {
|
||||||
|
try {
|
||||||
|
_ = typeof(IJob).GetMethod(nameof(IJob.ExecuteAsync))!.Invoke( //
|
||||||
|
Activator.CreateInstance(job.Key), [CancellationToken.None]);
|
||||||
|
job.Value.LastExecutionTime = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
LogHelper.Get<IServiceCollection>().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
using Gurion.Schedule;
|
|
||||||
using NetAdmin.Host.BackgroundRunning;
|
using NetAdmin.Host.BackgroundRunning;
|
||||||
using NetAdmin.Host.Middlewares;
|
using NetAdmin.Infrastructure.Schedule;
|
||||||
|
|
||||||
namespace NetAdmin.SysComponent.Host.Jobs;
|
namespace NetAdmin.SysComponent.Host.Jobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 释放计划作业
|
/// 释放计划作业
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JobConfig(TriggerCron = "0 * * * * *")]
|
||||||
public sealed class FreeScheduledJob : WorkBase<FreeScheduledJob>, IJob
|
public sealed class FreeScheduledJob : WorkBase<FreeScheduledJob>, IJob
|
||||||
{
|
{
|
||||||
private readonly IJobService _jobService;
|
private readonly IJobService _jobService;
|
||||||
@ -22,17 +22,11 @@ public sealed class FreeScheduledJob : WorkBase<FreeScheduledJob>, IJob
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 具体处理逻辑
|
/// 具体处理逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">作业执行前上下文</param>
|
/// <param name="cancelToken">取消任务 Token</param>
|
||||||
/// <param name="stoppingToken">取消任务 Token</param>
|
|
||||||
/// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
|
/// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
|
||||||
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
|
public async Task ExecuteAsync(CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
if (SafetyShopHostMiddleware.IsShutdown) {
|
await WorkflowAsync(true, cancelToken).ConfigureAwait(false);
|
||||||
Console.WriteLine(Ln.此节点已下线);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await WorkflowAsync(true, stoppingToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
using FreeSql.Internal;
|
using FreeSql.Internal;
|
||||||
using Gurion.RemoteRequest;
|
using Gurion.RemoteRequest;
|
||||||
using Gurion.RemoteRequest.Extensions;
|
using Gurion.RemoteRequest.Extensions;
|
||||||
using Gurion.Schedule;
|
|
||||||
using NetAdmin.Application.Extensions;
|
using NetAdmin.Application.Extensions;
|
||||||
using NetAdmin.Domain.Dto.Sys.Job;
|
using NetAdmin.Domain.Dto.Sys.Job;
|
||||||
using NetAdmin.Domain.Dto.Sys.JobRecord;
|
using NetAdmin.Domain.Dto.Sys.JobRecord;
|
||||||
using NetAdmin.Host.BackgroundRunning;
|
using NetAdmin.Host.BackgroundRunning;
|
||||||
using NetAdmin.Host.Middlewares;
|
using NetAdmin.Infrastructure.Schedule;
|
||||||
|
|
||||||
namespace NetAdmin.SysComponent.Host.Jobs;
|
namespace NetAdmin.SysComponent.Host.Jobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 计划作业
|
/// 计划作业
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JobConfig(TriggerCron = "* * * * * *")]
|
||||||
public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
|
public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
|
||||||
{
|
{
|
||||||
private static string _accessToken;
|
private static string _accessToken;
|
||||||
@ -30,19 +30,12 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 具体处理逻辑
|
/// 具体处理逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">作业执行前上下文</param>
|
/// <param name="cancelToken">取消任务 Token</param>
|
||||||
/// <param name="stoppingToken">取消任务 Token</param>
|
|
||||||
/// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
|
/// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
|
||||||
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
|
public Task ExecuteAsync(CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
if (SafetyShopHostMiddleware.IsShutdown) {
|
return Parallel.ForAsync(0, Numbers.SCHEDULED_JOB_PARALLEL_NUM, cancelToken
|
||||||
Console.WriteLine(Ln.此节点已下线);
|
, async (_, _) => await WorkflowAsync(cancelToken).ConfigureAwait(false));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once MethodSupportsCancellation
|
|
||||||
await Parallel.ForAsync(0, Numbers.SCHEDULED_JOB_PARALLEL_NUM, async (_, _) => await WorkflowAsync(stoppingToken).ConfigureAwait(false))
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user