86 lines
2.9 KiB
C#
86 lines
2.9 KiB
C#
using MediatR;
|
||
using Microsoft.Extensions.Options;
|
||
using Workflow.Application.BuildFlow.Imports.Commands;
|
||
using Workflow.Application.Reliability;
|
||
|
||
namespace BuildFlow.Host.Services.BackgroundServices;
|
||
|
||
/// <summary>
|
||
/// 周期触发导入任务原子领取命令的 Host 后台服务。
|
||
/// </summary>
|
||
public sealed class ImportJobProcessorService(
|
||
IServiceScopeFactory scopeFactory,
|
||
IOptionsMonitor<WorkflowJobOptions> options,
|
||
ILogger<ImportJobProcessorService> logger) : BackgroundService
|
||
{
|
||
private readonly string _leaseOwner =
|
||
$"import:{Environment.MachineName}:{Environment.ProcessId}:{Guid.NewGuid():N}";
|
||
|
||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||
{
|
||
var importOptions = options.CurrentValue.Import;
|
||
logger.LogInformation(
|
||
"ImportJobProcessor 启动,Owner={LeaseOwner},轮询间隔 {Interval}s",
|
||
_leaseOwner,
|
||
importOptions.PollIntervalSeconds);
|
||
|
||
try
|
||
{
|
||
await Task.Delay(
|
||
TimeSpan.FromSeconds(importOptions.StartupDelaySeconds),
|
||
stoppingToken);
|
||
using var timer = new PeriodicTimer(
|
||
TimeSpan.FromSeconds(importOptions.PollIntervalSeconds));
|
||
|
||
do
|
||
{
|
||
try
|
||
{
|
||
await RunScanAsync(stoppingToken);
|
||
}
|
||
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||
{
|
||
break;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "ImportJobProcessor 单轮处理异常");
|
||
}
|
||
}
|
||
while (await timer.WaitForNextTickAsync(stoppingToken));
|
||
}
|
||
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||
{
|
||
// 正常停机。
|
||
}
|
||
|
||
logger.LogInformation("ImportJobProcessor 停止,Owner={LeaseOwner}", _leaseOwner);
|
||
}
|
||
|
||
/// <summary>执行一轮导入任务领取和处理。</summary>
|
||
public async Task<int> RunScanAsync(CancellationToken ct = default)
|
||
{
|
||
using var scope = scopeFactory.CreateScope();
|
||
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
||
var importOptions = options.CurrentValue.Import;
|
||
var processed = await mediator.Send(
|
||
new ProcessAvailableImportJobsCommand(
|
||
_leaseOwner,
|
||
importOptions.BatchSize,
|
||
TimeSpan.FromSeconds(importOptions.LeaseSeconds),
|
||
importOptions.MaxAttempts,
|
||
importOptions.RowsPerBatch),
|
||
ct);
|
||
|
||
if (processed > 0)
|
||
{
|
||
logger.LogInformation(
|
||
"ImportJobProcessor 完成一轮处理,Owner={LeaseOwner},领取 {Count} 个任务",
|
||
_leaseOwner,
|
||
processed);
|
||
}
|
||
|
||
return processed;
|
||
}
|
||
}
|