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

Quartz Scheduler多任务部署为Windows服务后仅首个任务运行问题

排查Windows服务中Quartz仅第一个Job运行的问题

结合你的代码和场景(Debug正常、Release部署后仅第一个Job运行且无错误日志),我之前也碰到过类似的情况,主要是以下几个常见原因导致的,你可以逐一排查:

1. Async Void 在服务生命周期方法中的隐患

Windows服务的OnStart和OnStop是同步方法,当你用async void修饰时,异步操作会脱离服务控制管理器的监控:

  • 当代码执行到第一个await时,OnStart会立即返回,服务控制管理器会认为服务已经启动完成,但后续的Job调度代码可能还在后台执行;
  • 如果后续的异步操作抛出异常,async void会将异常直接抛到线程池,不会被你的log4net捕获,导致日志中看不到错误。

解决办法

将异步逻辑抽离到单独的async Task方法中,在OnStart里同步等待完成,同时添加全局异常捕获:

protected override void OnStart(string[] args)
{
    _logFile.Info("Service OnStart");
    try
    {
        // 同步等待异步启动逻辑完成
        StartSchedulerAsync(args).GetAwaiter().GetResult();
        _logFile.Info("End Service OnStart");
    }
    catch (Exception ex)
    {
        _logFile.Error("Failed to start scheduler service", ex);
        // 抛出异常让服务控制管理器感知启动失败
        throw;
    }
}

private async Task StartSchedulerAsync(string[] args)
{
    var factory = new StdSchedulerFactory();
    scheduler = await factory.GetScheduler();
    await scheduler.Start();

    // 调度SearchJob
    var job = JobBuilder.Create<SearchJob>()
        .WithIdentity("SearchJob", "group1")
        .Build();
    var trigger = TriggerBuilder.Create()
        .WithIdentity("SearchJobtrigger", "group1")
        .StartNow()
        .WithCronSchedule(Settings.Default.SearchCronExp)
        .ForJob(job)
        .Build();
    await scheduler.ScheduleJob(job, trigger);
    _logFile.Info("SearchJob scheduled successfully");

    // 调度UploadJob
    var jobUpload = JobBuilder.Create<UploadJob>()
        .WithIdentity("UploadJob", "group1")
        .Build();
    var triggerUpload = TriggerBuilder.Create()
        .WithIdentity("TriggerUploadJob", "group1")
        .StartNow()
        .WithCronSchedule(Settings.Default.UploadCronExp)
        .ForJob(jobUpload)
        .Build();
    await scheduler.ScheduleJob(jobUpload, triggerUpload);
    _logFile.Info("UploadJob scheduled successfully");

    // 调度AwaiterJob
    var jobAwaiter = JobBuilder.Create<AwaiterJob>()
        .WithIdentity("AwaiterJob", "group1")
        .Build();
    var triggerAwaiterJob = TriggerBuilder.Create()
        .WithIdentity("triggerAwaiterJob", "group1")
        .StartNow()
        .WithCronSchedule(Settings.Default.AwaiterCronExp)
        .ForJob(jobAwaiter)
        .Build();
    await scheduler.ScheduleJob(jobAwaiter, triggerAwaiterJob);
    _logFile.Info("AwaiterJob scheduled successfully");
}

protected override void OnStop()
{
    _logFile.Info("Service OnStop");
    try
    {
        scheduler?.Shutdown().GetAwaiter().GetResult();
    }
    catch (Exception ex)
    {
        _logFile.Error("Failed to shutdown scheduler", ex);
    }
}

2. Release模式下配置文件读取异常

Debug和Release模式使用的配置文件是分开的,很可能你部署Release版本时,Settings.Default中的UploadCronExp或AwaiterCronExp为空、无效,或者配置文件没有被正确复制到服务安装目录:

  • Quartz的WithCronSchedule如果传入无效表达式,会抛出异常,但因为之前async void的问题,异常没有被记录;
  • 如果Cron表达式为空,Quartz也会静默失败,不会触发Job。

解决办法

  • 验证Release版本的app.config(或YourServiceName.exe.config)中是否存在正确的Cron表达式;
  • 在代码中添加Cron表达式有效性校验,提前捕获错误:
// 校验UploadJob的Cron表达式
var uploadCron = Settings.Default.UploadCronExp;
if (string.IsNullOrWhiteSpace(uploadCron) || !CronExpression.IsValidExpression(uploadCron))
{
    var errorMsg = $"Invalid or empty cron expression for UploadJob: {uploadCron}";
    _logFile.Error(errorMsg);
    throw new ArgumentException(errorMsg);
}

3. Job类的访问权限或构造函数问题

Quartz通过反射创建Job实例,如果你的UploadJob或AwaiterJob是internal修饰,或者没有无参构造函数,在Release模式下(编译器会做优化,反射权限更严格)可能创建失败:

  • Debug模式下编译器的调试特性会放宽反射限制,所以能正常创建;
  • Release模式下反射无法访问internal类,导致Job无法实例化,进而不会被调度。

解决办法

确保所有Job类是public修饰,并且有无参构造函数:

public class UploadJob : IJob
{
    // 必须有无参构造函数,Quartz需要通过反射实例化
    public UploadJob() { }

    public async Task Execute(IJobExecutionContext context)
    {
        // Job逻辑
        _logFile.Info("UploadJob executed");
    }
}

4. 日志范围不足,未记录Job实例化/调度异常

之前的代码中,只有启动和停止的日志,没有每个Job调度的成功日志,也没有捕获单个Job调度的异常。即使某个Job调度失败,你也看不到具体原因。

解决办法

在每个Job调度完成后添加成功日志,并且为每个Job的调度逻辑单独添加try-catch,捕获并记录异常(如上面第一个解决办法中的代码示例)。


内容的提问来源于stack exchange,提问作者r.pezzolati

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 12:08:11