Support distributed crontab workers.
This commit is contained in:
parent
3c1f1bba57
commit
fc8d8d92b2
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace BotSharp.Core.Rules.Triggers;
|
||||
namespace BotSharp.Abstraction.Rules;
|
||||
|
||||
public interface IRuleTrigger
|
||||
{
|
||||
|
|
@ -2,6 +2,9 @@ namespace BotSharp.Core.Crontab.Abstraction;
|
|||
|
||||
public interface ICrontabHook
|
||||
{
|
||||
string[]? Triggers
|
||||
=> null;
|
||||
|
||||
Task OnCronTriggered(CrontabItem item)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,5 +34,6 @@ public class CrontabPlugin : IBotSharpPlugin
|
|||
services.AddScoped<IAgentUtilityHook, CrontabUtilityHook>();
|
||||
services.AddScoped<ICrontabService, CrontabService>();
|
||||
services.AddHostedService<CrontabWatcher>();
|
||||
services.AddHostedService<CrontabEventSubscription>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
using BotSharp.Abstraction.Infrastructures.Events;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BotSharp.Core.Crontab.Services;
|
||||
|
||||
public class CrontabEventSubscription : BackgroundService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public CrontabEventSubscription(IServiceProvider services, ILogger<CrontabEventSubscription> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation("Crontab event subscription background service is running.");
|
||||
|
||||
using (var scope = _services.CreateScope())
|
||||
{
|
||||
var subscriber = scope.ServiceProvider.GetRequiredService<IEventSubscriber>();
|
||||
var cron = scope.ServiceProvider.GetRequiredService<ICrontabService>();
|
||||
var crons = await cron.GetCrontable();
|
||||
foreach (var item in crons)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await subscriber.SubscribeAsync($"Crontab:{item.Title}",
|
||||
"Crontab",
|
||||
port: 0,
|
||||
priorityEnabled: false, async (sender, args) =>
|
||||
{
|
||||
var scope = _services.CreateScope();
|
||||
cron = scope.ServiceProvider.GetRequiredService<ICrontabService>();
|
||||
await cron.ScheduledTimeArrived(item);
|
||||
}, stoppingToken: stoppingToken);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*using (var scope = _services.CreateScope())
|
||||
{
|
||||
var cron = scope.ServiceProvider.GetRequiredService<ICrontabService>();
|
||||
var crons = await cron.GetCrontable();
|
||||
|
||||
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
@ -55,12 +55,15 @@ public class CrontabService : ICrontabService
|
|||
public async Task ScheduledTimeArrived(CrontabItem item)
|
||||
{
|
||||
_logger.LogDebug($"ScheduledTimeArrived {item}");
|
||||
|
||||
|
||||
await HookEmitter.Emit<ICrontabHook>(_services, async hook =>
|
||||
{
|
||||
await hook.OnTaskExecuting(item);
|
||||
await hook.OnCronTriggered(item);
|
||||
await hook.OnTaskExecuted(item);
|
||||
if (hook.Triggers == null || hook.Triggers.Contains(item.Title))
|
||||
{
|
||||
await hook.OnTaskExecuting(item);
|
||||
await hook.OnCronTriggered(item);
|
||||
await hook.OnTaskExecuted(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.Infrastructures;
|
||||
using BotSharp.Abstraction.Infrastructures.Events;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NCrontab;
|
||||
|
|
@ -26,9 +27,9 @@ public class CrontabWatcher : BackgroundService
|
|||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var delay = Task.Delay(1000 * 10, stoppingToken);
|
||||
var delay = Task.Delay(1000, stoppingToken);
|
||||
|
||||
await locker.LockAsync("CrontabWatcher", async () =>
|
||||
await locker.LockAsync("CrontabWatcher:locker", async () =>
|
||||
{
|
||||
await RunCronChecker(scope.ServiceProvider);
|
||||
});
|
||||
|
|
@ -44,8 +45,13 @@ public class CrontabWatcher : BackgroundService
|
|||
{
|
||||
var cron = services.GetRequiredService<ICrontabService>();
|
||||
var crons = await cron.GetCrontable();
|
||||
|
||||
var publisher = services.GetRequiredService<IEventPublisher>();
|
||||
|
||||
foreach (var item in crons)
|
||||
{
|
||||
_logger.LogDebug($"[{DateTime.UtcNow}] Cron task ({item.Title}, {item.Cron}), Last Execution Time: {item.LastExecutionTime}");
|
||||
|
||||
try
|
||||
{
|
||||
// strip seconds from cron expression
|
||||
|
|
@ -80,8 +86,10 @@ public class CrontabWatcher : BackgroundService
|
|||
|
||||
if (matches)
|
||||
{
|
||||
_logger.LogDebug($"The current time matches the cron expression {item}");
|
||||
cron.ScheduledTimeArrived(item);
|
||||
_logger.LogInformation($"The current time matches the cron expression {item}");
|
||||
|
||||
await publisher.PublishAsync($"Crontab:{item.Title}", item.Cron);
|
||||
// cron.ScheduledTimeArrived(item);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -97,9 +105,9 @@ public class CrontabWatcher : BackgroundService
|
|||
var nextOccurrence = schedule.GetNextOccurrence(DateTime.UtcNow);
|
||||
var afterNextOccurrence = schedule.GetNextOccurrence(nextOccurrence);
|
||||
var interval = afterNextOccurrence - nextOccurrence;
|
||||
if (interval.TotalMinutes < 10)
|
||||
if (interval.TotalMinutes < 1)
|
||||
{
|
||||
throw new ArgumentException("The minimum interval must be at least 10 minutes.");
|
||||
throw new ArgumentException("The minimum interval must be at least 1 minutes.");
|
||||
}
|
||||
return nextOccurrence - interval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
using BotSharp.Core.Rules.Triggers;
|
||||
|
||||
namespace BotSharp.Core.Rules.Engines;
|
||||
|
||||
public interface IRuleEngine
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
using BotSharp.Abstraction.Conversations;
|
||||
using BotSharp.Abstraction.Conversations.Enums;
|
||||
using BotSharp.Abstraction.Models;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Utilities;
|
||||
using BotSharp.Core.Rules.Triggers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Data;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,4 +6,6 @@ global using BotSharp.Abstraction.Plugins;
|
|||
global using BotSharp.Abstraction.Agents;
|
||||
global using BotSharp.Abstraction.Conversations.Models;
|
||||
global using BotSharp.Abstraction.Instructs;
|
||||
global using BotSharp.Abstraction.Instructs.Models;
|
||||
global using BotSharp.Abstraction.Instructs.Models;
|
||||
|
||||
global using BotSharp.Abstraction.Rules;
|
||||
|
|
@ -33,7 +33,7 @@ public class RedisPublisher : IEventPublisher
|
|||
|
||||
if (CheckMessageExists(db, channel, "message", message))
|
||||
{
|
||||
_logger.LogError($"The message already exists {channel} {message}");
|
||||
_logger.LogInformation($"The message already exists {channel} {message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +84,8 @@ public class RedisPublisher : IEventPublisher
|
|||
return
|
||||
[
|
||||
new NameValueEntry("message", message),
|
||||
new NameValueEntry("timestamp", DateTime.UtcNow.ToString("o"))
|
||||
new NameValueEntry("timestamp", DateTime.UtcNow.ToString("o")),
|
||||
new NameValueEntry("machine", Environment.MachineName)
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +95,7 @@ public class RedisPublisher : IEventPublisher
|
|||
[
|
||||
new NameValueEntry("message", message),
|
||||
new NameValueEntry("timestamp", DateTime.UtcNow.ToString("o")),
|
||||
new NameValueEntry("machine", Environment.MachineName),
|
||||
new NameValueEntry("error", error)
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Rules;
|
||||
using BotSharp.Core.Rules.Triggers;
|
||||
|
||||
namespace BotSharp.OpenAPI.Controllers;
|
||||
|
|
|
|||
|
|
@ -188,6 +188,11 @@ public class HandleHttpRequestFn : IFunctionCallback
|
|||
{
|
||||
if (response == null) return string.Empty;
|
||||
|
||||
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
return response.ReasonPhrase ?? "http call has error occurred.";
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue