You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Linux上的ASP.NET应用持续运行?解决RedHat重启丢进度问题

解决Hangfire非幂等任务重启丢失进度问题

针对RedHat系统中.NET Core Hangfire应用重启后丢失长期非幂等任务进度的问题,可从以下几个维度解决:

1. 优化systemd服务配置,实现优雅停止

当前systemd配置缺少优雅停止的关键设置,导致应用被强制终止时任务进度直接丢失。修改[Service]段配置,给任务足够的收尾时间:

[Unit]
Description=ABC

[Service]
WorkingDirectory=/var/www/html/ABC
ExecStart=/usr/bin/dotnet /var/www/html/ABC/XYZ.DLL
Restart=always
# 设置优雅停止超时(示例为5分钟,可根据任务实际时长调整)
TimeoutStopSec=300
# 发送SIGTERM信号触发.NET Core优雅关闭流程
KillSignal=SIGTERM
# 先等待主进程处理停止信号,超时后再强制终止
KillMode=mixed
# 重启前短暂等待,避免频繁重启导致的资源浪费
RestartSec=5

Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target

修改后执行以下命令使配置生效:

sudo systemctl daemon-reload
sudo systemctl restart ABC.service

2. 给非幂等任务添加进度持久化逻辑

由于任务不具备幂等性,必须在执行过程中定期将进度保存到持久化存储(如数据库、Redis),确保重启后能从断点继续执行:

  • 任务执行时,每完成一个阶段就更新进度记录(比如已处理的数据ID、当前执行步骤)
  • 通过Hangfire过滤器在任务启动时检查是否存在未完成的进度,存在则直接从断点恢复

示例代码片段:

// 进度持久化过滤器
public class TaskProgressFilter : IPerformingFilter, IPerformedFilter
{
    private readonly AppDbContext _dbContext;

    public TaskProgressFilter(AppDbContext dbContext) => _dbContext = dbContext;

    public void OnPerforming(PerformingContext context)
    {
        var jobId = context.BackgroundJob.Id;
        var progress = _dbContext.TaskProgresses.FirstOrDefault(p => p.JobId == jobId);
        if (progress != null)
        {
            context.Items["TaskProgress"] = progress;
        }
    }

    public void OnPerformed(PerformedContext context)
    {
        var jobId = context.BackgroundJob.Id;
        var progress = context.Items["TaskProgress"] as TaskProgress;
        if (progress != null)
        {
            _dbContext.TaskProgresses.Remove(progress);
            _dbContext.SaveChanges();
        }
    }
}

// 非幂等任务实现
public async Task LongRunningTask(PerformContext context)
{
    var progress = context.Items["TaskProgress"] as TaskProgress;
    int startId = progress?.LastProcessedId ?? 0;

    var dataItems = await _dbContext.Data.Where(d => d.Id > startId).ToListAsync();
    foreach (var item in dataItems)
    {
        // 执行业务处理逻辑
        await ProcessDataItem(item);

        // 每处理100条记录保存一次进度,可根据业务调整频率
        if (item.Id % 100 == 0)
        {
            await SaveTaskProgress(context.BackgroundJob.Id, item.Id);
        }
    }
}

// 在Program.cs中注册过滤器和Hangfire存储
builder.Services.AddHangfire(config =>
{
    config.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireDb"));
    config.GlobalFilters.Add(new TaskProgressFilter(builder.Services.BuildServiceProvider().GetRequiredService<AppDbContext>()));
});

3. 确保Hangfire使用持久化存储

绝对禁止使用Hangfire的内存存储,内存存储在应用重启时会丢失所有任务数据。必须配置数据库或Redis作为持久化存储:

// Program.cs中配置Hangfire存储
builder.Services.AddHangfire(config =>
{
    // 使用SQL Server作为存储
    config.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireDb"));
    // 或使用Redis作为存储
    // config.UseRedisStorage("localhost:6379,password=yourRedisPassword");
});

4. 配置Hangfire服务端避免重复执行已处理内容

禁用正在执行任务的自动重试逻辑,防止应用重启后重复执行已经处理过的任务片段:

builder.Services.AddHangfireServer(options =>
{
    options.WorkerCount = Environment.ProcessorCount * 2;
    // 调整调度轮询间隔,减少不必要的资源消耗
    options.SchedulePollingInterval = TimeSpan.FromSeconds(15);
});

内容的提问来源于stack exchange,提问作者Nabeel Riaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:05:01