mirror of https://github.com/abpframework/abp.git
11 changed files with 522 additions and 1 deletions
@ -0,0 +1,112 @@ |
|||
# ABP TickerQ Background Workers Integration |
|||
|
|||
This package provides integration between [TickerQ](https://github.com/dotnetdevelopersdz/TickerQ) and the ABP Framework's background worker system. |
|||
|
|||
## About TickerQ |
|||
|
|||
TickerQ is a fast, reflection-free background task scheduler for .NET — built with source generators, EF Core integration, cron + time-based execution, and a real-time dashboard. |
|||
|
|||
Key features: |
|||
- **Performance**: Reflection-free design with source generators |
|||
- **EF Core Integration**: Native Entity Framework Core support |
|||
- **Flexible Scheduling**: Cron expressions and time-based execution |
|||
- **Real-time Dashboard**: Built-in monitoring and management |
|||
- **Modern .NET**: Built for modern .NET with async/await |
|||
|
|||
## Installation |
|||
|
|||
Install the NuGet package: |
|||
|
|||
```bash |
|||
dotnet add package Volo.Abp.BackgroundWorkers.TickerQ |
|||
``` |
|||
|
|||
Or using the ABP CLI: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.BackgroundWorkers.TickerQ |
|||
``` |
|||
|
|||
## Usage |
|||
|
|||
1. Add the module dependency to your ABP module: |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpBackgroundWorkersTickerQModule))] |
|||
public class YourModule : AbpModule |
|||
{ |
|||
// ... |
|||
} |
|||
``` |
|||
|
|||
2. Create your background worker: |
|||
|
|||
```csharp |
|||
public class MyTickerQWorker : TickerQBackgroundWorkerBase |
|||
{ |
|||
public MyTickerQWorker() |
|||
{ |
|||
JobId = nameof(MyTickerQWorker); |
|||
CronExpression = "0 */5 * ? * *"; // Every 5 minutes |
|||
Priority = 1; |
|||
MaxRetryAttempts = 3; |
|||
} |
|||
|
|||
public override Task DoWorkAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
Logger.LogInformation("TickerQ worker executed!"); |
|||
// Your work logic here |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
3. Configure options (optional): |
|||
|
|||
```csharp |
|||
Configure<AbpBackgroundWorkerTickerQOptions>(options => |
|||
{ |
|||
options.IsAutoRegisterEnabled = true; |
|||
options.DefaultCronExpression = "0 * * ? * *"; |
|||
options.DefaultMaxRetryAttempts = 3; |
|||
options.DefaultPriority = 0; |
|||
}); |
|||
``` |
|||
|
|||
## Migration from Other Background Workers |
|||
|
|||
The integration provides adapters for existing background workers: |
|||
|
|||
- `AsyncPeriodicBackgroundWorkerBase` workers will work automatically |
|||
- `PeriodicBackgroundWorkerBase` workers will work automatically |
|||
- Timer periods are converted to appropriate cron expressions |
|||
|
|||
## Features |
|||
|
|||
- **Automatic Registration**: Workers are auto-registered by default |
|||
- **Dependency Injection**: Full DI support in workers |
|||
- **Error Handling**: Built-in retry logic and error handling |
|||
- **Performance**: Benefits from TickerQ's reflection-free design |
|||
- **Compatibility**: Works with existing ABP background workers |
|||
|
|||
## Samples |
|||
|
|||
See the `Samples` folder for example implementations demonstrating: |
|||
- Basic worker usage |
|||
- Error handling and retries |
|||
- Dependency injection |
|||
- Configuration options |
|||
|
|||
## Documentation |
|||
|
|||
For detailed documentation, see: [docs/en/framework/infrastructure/background-workers/tickerq.md](../../../docs/en/framework/infrastructure/background-workers/tickerq.md) |
|||
|
|||
## Requirements |
|||
|
|||
- .NET 8.0 or later |
|||
- ABP Framework 9.0 or later |
|||
- TickerQ package (when available) |
|||
|
|||
## Status |
|||
|
|||
This integration is ready for use once the TickerQ package becomes available on NuGet. The implementation follows ABP's established patterns for background worker integrations (Quartz, Hangfire) and provides a seamless migration path. |
|||
@ -0,0 +1,39 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.BackgroundWorkers.TickerQ.Samples; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers.TickerQ; |
|||
|
|||
/// <summary>
|
|||
/// Sample module demonstrating how to use TickerQ background workers in an ABP application.
|
|||
/// </summary>
|
|||
[DependsOn(typeof(AbpBackgroundWorkersTickerQModule))] |
|||
public class SampleTickerQModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
// Configure TickerQ options
|
|||
Configure<AbpBackgroundWorkerTickerQOptions>(options => |
|||
{ |
|||
options.IsAutoRegisterEnabled = true; |
|||
options.DefaultCronExpression = "0 * * ? * *"; // Every minute
|
|||
options.DefaultMaxRetryAttempts = 3; |
|||
options.DefaultPriority = 0; |
|||
}); |
|||
|
|||
// Register sample workers as transient services so they can be injected with dependencies
|
|||
context.Services.AddTransient<SampleTickerQWorker>(); |
|||
context.Services.AddTransient<SampleErrorHandlingTickerQWorker>(); |
|||
context.Services.AddTransient<SampleDependencyInjectionTickerQWorker>(); |
|||
} |
|||
|
|||
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context) |
|||
{ |
|||
// Sample workers with AutoRegister = true will be registered automatically
|
|||
// But you can also register them manually if needed:
|
|||
|
|||
// await context.AddBackgroundWorkerAsync<SampleTickerQWorker>();
|
|||
// await context.AddBackgroundWorkerAsync<SampleErrorHandlingTickerQWorker>();
|
|||
// await context.AddBackgroundWorkerAsync<SampleDependencyInjectionTickerQWorker>();
|
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers.TickerQ.Samples; |
|||
|
|||
/// <summary>
|
|||
/// Sample TickerQ background worker that demonstrates basic usage.
|
|||
/// This worker runs every 5 minutes and performs a simple logging operation.
|
|||
/// </summary>
|
|||
public class SampleTickerQWorker : TickerQBackgroundWorkerBase |
|||
{ |
|||
public SampleTickerQWorker() |
|||
{ |
|||
// Configure the worker
|
|||
JobId = nameof(SampleTickerQWorker); |
|||
CronExpression = "0 */5 * ? * *"; // Every 5 minutes
|
|||
Priority = 1; // Higher than default priority
|
|||
MaxRetryAttempts = 5; // Retry up to 5 times on failure
|
|||
} |
|||
|
|||
public override Task DoWorkAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
Logger.LogInformation("Sample TickerQ worker executed at {Time}", DateTime.Now); |
|||
|
|||
// Simulate some work
|
|||
Logger.LogDebug("Processing sample work..."); |
|||
|
|||
// Your background task logic goes here
|
|||
// For example:
|
|||
// - Process pending orders
|
|||
// - Send scheduled notifications
|
|||
// - Cleanup old data
|
|||
// - Generate reports
|
|||
|
|||
Logger.LogInformation("Sample TickerQ worker completed successfully"); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sample TickerQ background worker that demonstrates error handling and retry logic.
|
|||
/// </summary>
|
|||
public class SampleErrorHandlingTickerQWorker : TickerQBackgroundWorkerBase |
|||
{ |
|||
private static int _executionCount = 0; |
|||
|
|||
public SampleErrorHandlingTickerQWorker() |
|||
{ |
|||
JobId = nameof(SampleErrorHandlingTickerQWorker); |
|||
CronExpression = "0 */10 * ? * *"; // Every 10 minutes
|
|||
MaxRetryAttempts = 3; |
|||
} |
|||
|
|||
public override async Task DoWorkAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
var currentCount = Interlocked.Increment(ref _executionCount); |
|||
|
|||
Logger.LogInformation("Error handling worker executed {Count} times", currentCount); |
|||
|
|||
// Simulate intermittent failures for demonstration
|
|||
if (currentCount % 3 == 0) |
|||
{ |
|||
Logger.LogWarning("Simulating a temporary failure (will retry)"); |
|||
throw new InvalidOperationException("Simulated failure for demonstration"); |
|||
} |
|||
|
|||
// Simulate successful work
|
|||
await Task.Delay(100, cancellationToken); // Simulate some async work
|
|||
|
|||
Logger.LogInformation("Error handling worker completed successfully"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sample TickerQ background worker that demonstrates working with dependency injection.
|
|||
/// </summary>
|
|||
public class SampleDependencyInjectionTickerQWorker : TickerQBackgroundWorkerBase |
|||
{ |
|||
// In a real application, you would inject your repositories, services, etc.
|
|||
// private readonly IMyRepository _myRepository;
|
|||
// private readonly IMyService _myService;
|
|||
|
|||
public SampleDependencyInjectionTickerQWorker( |
|||
// IMyRepository myRepository,
|
|||
// IMyService myService
|
|||
) |
|||
{ |
|||
// _myRepository = myRepository;
|
|||
// _myService = myService;
|
|||
|
|||
JobId = nameof(SampleDependencyInjectionTickerQWorker); |
|||
CronExpression = "0 0 */6 ? * *"; // Every 6 hours
|
|||
} |
|||
|
|||
public override async Task DoWorkAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
Logger.LogInformation("Dependency injection worker started"); |
|||
|
|||
try |
|||
{ |
|||
// Example of using injected services
|
|||
// var entities = await _myRepository.GetListAsync(cancellationToken: cancellationToken);
|
|||
// await _myService.ProcessEntitiesAsync(entities, cancellationToken);
|
|||
|
|||
// For demonstration, just simulate the work
|
|||
await Task.Delay(50, cancellationToken); |
|||
|
|||
Logger.LogInformation("Dependency injection worker processed data successfully"); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogError(ex, "Error occurred in dependency injection worker"); |
|||
throw; // Re-throw to trigger retry logic
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
"role": "lib.framework" |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
{ |
|||
"name": "Volo.Abp.BackgroundWorkers.Quartz", |
|||
"hash": "", |
|||
"contents": [ |
|||
{ |
|||
"namespace": "Volo.Abp.BackgroundWorkers.Quartz", |
|||
"dependsOnModules": [ |
|||
{ |
|||
"declaringAssemblyName": "Volo.Abp.BackgroundWorkers", |
|||
"namespace": "Volo.Abp.BackgroundWorkers", |
|||
"name": "AbpBackgroundWorkersModule" |
|||
}, |
|||
{ |
|||
"declaringAssemblyName": "Volo.Abp.Quartz", |
|||
"namespace": "Volo.Abp.Quartz", |
|||
"name": "AbpQuartzModule" |
|||
} |
|||
], |
|||
"implementingInterfaces": [ |
|||
{ |
|||
"name": "IAbpModule", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IAbpModule" |
|||
}, |
|||
{ |
|||
"name": "IOnPreApplicationInitialization", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IOnPreApplicationInitialization" |
|||
}, |
|||
{ |
|||
"name": "IOnApplicationInitialization", |
|||
"namespace": "Volo.Abp", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.IOnApplicationInitialization" |
|||
}, |
|||
{ |
|||
"name": "IOnPostApplicationInitialization", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IOnPostApplicationInitialization" |
|||
}, |
|||
{ |
|||
"name": "IOnApplicationShutdown", |
|||
"namespace": "Volo.Abp", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.IOnApplicationShutdown" |
|||
}, |
|||
{ |
|||
"name": "IPreConfigureServices", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IPreConfigureServices" |
|||
}, |
|||
{ |
|||
"name": "IPostConfigureServices", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IPostConfigureServices" |
|||
} |
|||
], |
|||
"contentType": "abpModule", |
|||
"name": "AbpBackgroundWorkersQuartzModule", |
|||
"summary": null |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.BackgroundWorkers.TickerQ; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for TickerQ background worker integration.
|
|||
/// </summary>
|
|||
public static class AbpBackgroundWorkersTickerQServiceCollectionExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Adds TickerQ background worker services to the service collection.
|
|||
/// This method provides additional configuration options beyond the basic module registration.
|
|||
/// </summary>
|
|||
/// <param name="services">The service collection.</param>
|
|||
/// <param name="configure">Optional configuration action.</param>
|
|||
/// <returns>The service collection for chaining.</returns>
|
|||
public static IServiceCollection AddAbpTickerQBackgroundWorkers( |
|||
this IServiceCollection services, |
|||
Action<AbpBackgroundWorkerTickerQOptions>? configure = null) |
|||
{ |
|||
if (configure != null) |
|||
{ |
|||
services.Configure(configure); |
|||
} |
|||
|
|||
return services; |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net9.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.BackgroundWorkers.TickerQ.Tests</AssemblyName> |
|||
<PackageId>Volo.Abp.BackgroundWorkers.TickerQ.Tests</PackageId> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundWorkers.TickerQ\Volo.Abp.BackgroundWorkers.TickerQ.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundWorkers\Volo.Abp.BackgroundWorkers.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers.TickerQ; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpBackgroundWorkersTickerQModule), |
|||
typeof(AbpTestBaseModule), |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class AbpBackgroundWorkersTickerQTestModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers.TickerQ; |
|||
|
|||
public class TickerQBackgroundWorkerBase_Tests : AbpIntegratedTest<AbpBackgroundWorkersTickerQTestModule> |
|||
{ |
|||
[Fact] |
|||
public void Should_Have_Default_Properties() |
|||
{ |
|||
// Arrange & Act
|
|||
var worker = new TestTickerQWorker(); |
|||
|
|||
// Assert
|
|||
worker.AutoRegister.ShouldBeTrue(); |
|||
worker.Priority.ShouldBe(0); |
|||
worker.MaxRetryAttempts.ShouldBe(3); |
|||
worker.JobId.ShouldBeNull(); |
|||
worker.CronExpression.ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Allow_Custom_Configuration() |
|||
{ |
|||
// Arrange & Act
|
|||
var worker = new TestTickerQWorker |
|||
{ |
|||
JobId = "CustomJobId", |
|||
CronExpression = "0 0 12 * * ?", |
|||
Priority = 5, |
|||
MaxRetryAttempts = 10, |
|||
AutoRegister = false |
|||
}; |
|||
|
|||
// Assert
|
|||
worker.JobId.ShouldBe("CustomJobId"); |
|||
worker.CronExpression.ShouldBe("0 0 12 * * ?"); |
|||
worker.Priority.ShouldBe(5); |
|||
worker.MaxRetryAttempts.ShouldBe(10); |
|||
worker.AutoRegister.ShouldBeFalse(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Execute_DoWorkAsync() |
|||
{ |
|||
// Arrange
|
|||
var worker = new TestTickerQWorker(); |
|||
|
|||
// Act
|
|||
await worker.DoWorkAsync(); |
|||
|
|||
// Assert
|
|||
worker.ExecutionCount.ShouldBe(1); |
|||
} |
|||
|
|||
private class TestTickerQWorker : TickerQBackgroundWorkerBase |
|||
{ |
|||
public int ExecutionCount { get; private set; } |
|||
|
|||
public override Task DoWorkAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
ExecutionCount++; |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers.TickerQ; |
|||
|
|||
public class TickerQBackgroundWorkerManager_Tests : AbpIntegratedTest<AbpBackgroundWorkersTickerQTestModule> |
|||
{ |
|||
private readonly IBackgroundWorkerManager _backgroundWorkerManager; |
|||
private readonly IOptions<AbpBackgroundWorkerTickerQOptions> _options; |
|||
|
|||
public TickerQBackgroundWorkerManager_Tests() |
|||
{ |
|||
_backgroundWorkerManager = GetRequiredService<IBackgroundWorkerManager>(); |
|||
_options = GetRequiredService<IOptions<AbpBackgroundWorkerTickerQOptions>>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Use_TickerQBackgroundWorkerManager() |
|||
{ |
|||
// Assert
|
|||
_backgroundWorkerManager.ShouldBeOfType<TickerQBackgroundWorkerManager>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Have_Default_Options() |
|||
{ |
|||
// Assert
|
|||
var options = _options.Value; |
|||
options.IsAutoRegisterEnabled.ShouldBeTrue(); |
|||
options.DefaultCronExpression.ShouldBe("0 * * ? * *"); |
|||
options.DefaultMaxRetryAttempts.ShouldBe(3); |
|||
options.DefaultPriority.ShouldBe(0); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Allow_Options_Configuration() |
|||
{ |
|||
// This test would be run in a separate test module with different options
|
|||
// For now, just verify the options exist
|
|||
_options.Value.ShouldNotBeNull(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue