RabbitMQ 默认从单一回调通道提升为三类任务独立并行,跨类型并发能力由 1 提升到 3;每类还能独立配置 1–16 个消费者。[RabbitMqBackgroundJobs.cs (line 143)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/RabbitMqBackgroundJobs.cs:143) InMemory 开发模式同步改为按任务类型隔离队列,避免某类长任务堵塞其他任务。 租约恢复检查从“每发布一条执行一次”改为默认每 60 秒维护一次。 启动恢复改为数据库 NOT EXISTS 查询,不再把全部历史 Outbox 加载进内存。[BackgroundJobOutboxPublisher.cs (line 9)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/BackgroundJobOutboxPublisher.cs:9) 完成消息默认保留 14 天,之后按每批 500 条清理,并增加对应组合索引。 /health/messaging 现在返回各状态积压量、过期租约和最老任务等待时间。[BackgroundJobMonitoringService.cs (line 16)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/BackgroundJobMonitoringService.cs:16) 新增 Jiaowu.BackgroundJobs 运行时指标,覆盖发布量、处理量、发布耗时、处理耗时和清理量。[BackgroundJobTelemetry.cs (line 9)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/BackgroundJobs/BackgroundJobTelemetry.cs:9) 配置、Compose 和调优建议已更新。[README.md (line 257)](E:/jiaowu/README.md:257)
57 lines
2.2 KiB
C#
57 lines
2.2 KiB
C#
using Jiaowu.Api.Domain.System;
|
|
using Jiaowu.Api.Infrastructure.Persistence;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Jiaowu.Api.Infrastructure.BackgroundJobs;
|
|
|
|
public sealed record BackgroundJobBacklogSnapshot(
|
|
int Pending,
|
|
int Publishing,
|
|
int Published,
|
|
int Processing,
|
|
int ExpiredLeases,
|
|
DateTime? OldestUnfinishedAt,
|
|
double? OldestUnfinishedAgeSeconds);
|
|
|
|
public sealed class BackgroundJobMonitoringService(AppDbContext db)
|
|
{
|
|
public async Task<BackgroundJobBacklogSnapshot> GetSnapshotAsync(
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var counts = await db.BackgroundJobOutboxMessages.AsNoTracking()
|
|
.Where(x => x.State != BackgroundJobOutboxState.Completed)
|
|
.GroupBy(x => x.State)
|
|
.Select(group => new { State = group.Key, Count = group.Count() })
|
|
.ToListAsync(cancellationToken);
|
|
var countByState = counts.ToDictionary(x => x.State, x => x.Count);
|
|
var now = DateTime.UtcNow;
|
|
var expiredLeases = await db.BackgroundJobOutboxMessages.AsNoTracking()
|
|
.CountAsync(
|
|
x => (x.State == BackgroundJobOutboxState.Publishing ||
|
|
x.State == BackgroundJobOutboxState.Processing) &&
|
|
x.LeaseExpiresAt != null &&
|
|
x.LeaseExpiresAt < now,
|
|
cancellationToken);
|
|
var oldestUnfinishedAt = await db.BackgroundJobOutboxMessages
|
|
.AsNoTracking()
|
|
.Where(x => x.State != BackgroundJobOutboxState.Completed)
|
|
.Select(x => (DateTime?)x.CreatedAt)
|
|
.MinAsync(cancellationToken);
|
|
double? ageSeconds = oldestUnfinishedAt.HasValue
|
|
? Math.Max(0, (now - oldestUnfinishedAt.Value).TotalSeconds)
|
|
: null;
|
|
|
|
return new BackgroundJobBacklogSnapshot(
|
|
GetCount(BackgroundJobOutboxState.Pending),
|
|
GetCount(BackgroundJobOutboxState.Publishing),
|
|
GetCount(BackgroundJobOutboxState.Published),
|
|
GetCount(BackgroundJobOutboxState.Processing),
|
|
expiredLeases,
|
|
oldestUnfinishedAt,
|
|
ageSeconds);
|
|
|
|
int GetCount(BackgroundJobOutboxState state) =>
|
|
countByState.GetValueOrDefault(state);
|
|
}
|
|
}
|