【问题标题】:Hangfire - Recurring job can’t be scheduled, see inner exception for detailsHangfire - 无法安排重复作业,请参阅内部异常了解详细信息
【发布时间】:2021-04-05 11:08:15
【问题描述】:

我有一个应用程序;它位于三个不同的服务器上,使用负载均衡器进行用户分配。 该应用程序使用自己的队列,我为作业添加了一个过滤器,以保留他们的original 队列,以防它们在某个时候失败。但话又说回来,它继续表现得就像应用程序没有运行一样。错误如下;

System.InvalidOperationException: Recurring job can't be scheduled, see inner exception for details.
 ---> Hangfire.Common.JobLoadException: Could not load the job. See inner exception for the details.
 ---> System.IO.FileNotFoundException: Could not resolve assembly 'My.Api'.
   at System.TypeNameParser.ResolveAssembly(String asmName, Func`2 assemblyResolver, Boolean throwOnError, StackCrawlMark& stackMark)
   at System.TypeNameParser.ConstructType(Func`2 assemblyResolver, Func`4 typeResolver, Boolean throwOnError, Boolean ignoreCase, StackCrawlMark& stackMark)
   at System.TypeNameParser.GetType(String typeName, Func`2 assemblyResolver, Func`4 typeResolver, Boolean throwOnError, Boolean ignoreCase, StackCrawlMark& stackMark)
   at System.Type.GetType(String typeName, Func`2 assemblyResolver, Func`4 typeResolver, Boolean throwOnError)
   at Hangfire.Common.TypeHelper.DefaultTypeResolver(String typeName)
   at Hangfire.Storage.InvocationData.DeserializeJob()
   --- End of inner exception stack trace ---
   at Hangfire.Storage.InvocationData.DeserializeJob()
   at Hangfire.RecurringJobEntity..ctor(String recurringJobId, IDictionary`2 recurringJob, ITimeZoneResolver timeZoneResolver, DateTime now)
   --- End of inner exception stack trace ---
   at Hangfire.Server.RecurringJobScheduler.ScheduleRecurringJob(BackgroundProcessContext context, IStorageConnection connection, String recurringJobId, RecurringJobEntity recurringJob, DateTime now)
What can be the issue here? The apps are running. And once I trigger the recurring jobs, they are good to go, until they show the above error.

这是我的 AppStart 文件;

private IEnumerable<IDisposable> GetHangfireServers()
{
    Hangfire.GlobalConfiguration.Configuration
        .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
        .UseSimpleAssemblyNameTypeSerializer()
        .UseRecommendedSerializerSettings()
        .UseSqlServerStorage(HangfireServer, new SqlServerStorageOptions
        {
            CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
            SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
            QueuePollInterval = TimeSpan.Zero,
            UseRecommendedIsolationLevel = true,
            DisableGlobalLocks = true
        });

    yield return new BackgroundJobServer(new BackgroundJobServerOptions {
        Queues = new[] { "myapp" + GetEnvironmentName() },
        ServerName = "MyApp" + ConfigurationHelper.GetAppSetting("Environment")
    });
}

public void Configuration(IAppBuilder app)
{
    var container = new Container();
    container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
    
    RegisterTaskDependencies(container);
    container.RegisterWebApiControllers(System.Web.Http.GlobalConfiguration.Configuration);
    container.Verify();
    
    var configuration = new HttpConfiguration();
    configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
    
    /* HANGFIRE CONFIGURATION */
    if (Environment == "Production")
    {
        GlobalJobFilters.Filters.Add(new PreserveOriginalQueueAttribute());
        Hangfire.GlobalConfiguration.Configuration.UseActivator(new SimpleInjectorJobActivator(container));
        Hangfire.GlobalConfiguration.Configuration.UseLogProvider(new Api.HangfireArea.Helpers.CustomLogProvider(container.GetInstance<Core.Modules.LogModule>()));
        app.UseHangfireAspNet(GetHangfireServers);
        app.UseHangfireDashboard("/hangfire", new DashboardOptions
        {
            Authorization = new[] { new DashboardAuthorization() },
            AppPath = GetBackToSiteURL(),
            DisplayStorageConnectionString = false
        });
        AddOrUpdateJobs();
    }
    /* HANGFIRE CONFIGURATION */
    
    app.UseWebApi(configuration);
    
    WebApiConfig.Register(configuration);

}

public static void AddOrUpdateJobs()
{
    var queueName = "myapp" + GetEnvironmentName();
    RecurringJob.AddOrUpdate<HangfireArea.BackgroundJobs.AttachmentCreator>(
         "MyApp_MyTask",
         (service) => service.RunMyTask(), 
      "* * * * *", queue: queueName, timeZone: TimeZoneInfo.FindSystemTimeZoneById("Turkey Standard Time"));
}

这可能是什么问题?

【问题讨论】:

    标签: hangfire


    【解决方案1】:

    事实证明,当多个应用程序使用相同的 sql schema 时,Hangfire 本身并不能很好地工作。为了解决这个问题,我使用了Hangfire.MAMQSqlExtension。它是第三方扩展,但 repo 说它已被 Hangfire 官方认可。 如果您为多个应用使用相同的架构,您必须在您的所有应用中使用此扩展程序,否则您将面临上述错误。

    如果您的应用同时具有不同版本(例如 productiontestdevelopment),则此应用本身无法完全正常运行失败的工作。如果作业失败,常规 Hangfire 将不尊重它的原始队列,因此会将其移动到 default 队列。如果您的应用仅适用于您的应用的队列或者default 队列是共享的,这最终会产生问题。为了解决这个问题,为了强制 Hangfire 尊重原始队列属性,我使用了this 解决方案。效果很好,您可以根据 web.configappsettings.json 来命名应用的队列。

    我之前的回答由于某种原因被删除了?这解决了问题,没有其他办法。对于将遇到此问题的人,请勿删除答案。

    【讨论】:

      【解决方案2】:

      我发现的另一个选项是使用 Hangfire 的后台进程https://www.hangfire.io/overview.html#background-process

      public class CleanTempDirectoryProcess : IBackgroundProcess
      {
          public void Execute(BackgroundProcessContext context)
          {
              Directory.CleanUp(Directory.GetTempDirectory());
              context.Wait(TimeSpan.FromHours(1));
          }
      }
      

      并设置延迟。这解决了我的问题,因为我需要重复运行这项工作。我不确定这可能会对仪表板产生什么影响。

      【讨论】:

      • 我猜这个错误发生在不同的场景中,因为我的问题是通过相同的数据库模式运行多个应用程序。如果您的解决方案对此有所帮助,如何解决?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-09
      相关资源
      最近更新 更多