HangFire作为Windows服务用于.NET 6

gwbalxhn  于 2023-01-27  发布在  .NET
关注(0)|答案(1)|浏览(367)

我一直在寻找使用HangFire作为. NET 6的Windows服务,官方文档好像有10年的历史了。其他示例没有指定如何设置工作者服务。无论如何,这是我的环境--我有一个带有api app的Web应用程序。api app是后台作业将排队到HangFire的地方,但我希望实际的处理是在一个不同的服务器上,如应用程序服务器。所以我的目标是创建一个Windows服务,以简单地运行HangFire服务器,并继续让api应用程序来管理作业创建。
我创建了一个新的WorkerService项目,下面是我的代码:

public class Program
{
    public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();

    public static IHostBuilder CreateHostBuilder(string[] args) =>
       Host.CreateDefaultBuilder(args)
           .ConfigureLogging(logging =>
           {
               logging.ClearProviders();
               logging.AddConsole();
               logging.AddEventLog();
           })
           // Essential to run this as a window service
           .UseWindowsService()
           .ConfigureServices(configureServices);

    private static void configureServices(HostBuilderContext context, IServiceCollection services)
    {
        var defaultConnection = context.Configuration.GetConnectionString("DefaultConnection");
        var hangFireConnection = context.Configuration.GetConnectionString("HangFireConnection");
        AppSettings appSettings = context.Configuration.GetSection("AppSettings").Get<AppSettings>();

        services.AddLogging();
        services.AddHangfire(configuration => configuration
            .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
            .UseSimpleAssemblyNameTypeSerializer()
            .UseRecommendedSerializerSettings()
            .UseSqlServerStorage(hangFireConnection, new SqlServerStorageOptions
            {
                CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                QueuePollInterval = TimeSpan.Zero,
                UseRecommendedIsolationLevel = true,
                DisableGlobalLocks = true
            }));
        services.AddHangfireServer();

        services.AddDbContext<PpContext>(options => options.UseSqlServer(defaultConnection), ServiceLifetime.Transient);
        services.AddScoped<ExceptionNotifier>();
        services.AddHostedService<HangFireWorker>();

        JobStorage.Current = new SqlServerStorage(hangFireConnection);
        RecurringJob.AddOrUpdate<ExceptionNotifier>("exception-notification", x => x.NotifyByHour(), "0 * * * *"); //runs every hour on the hour
    }
}

正如您所看到的,我确实有一个每小时发生一次的循环作业。
对于HangFireWorker类,这是我所拥有的:

public class HangFireWorker : BackgroundService
{
    private readonly ILogger<HangFireWorker> _logger;

    public HangFireWorker(ILogger<HangFireWorker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        //while (!stoppingToken.IsCancellationRequested)
        //{
        //    _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
        //    await Task.Delay(1000, stoppingToken);
        //}

        //there is nothing to do here, hangfire already takes cares of all?
        await Task.Delay(0);
    }
}

所以我的问题是,我需要在主工作者类中做什么吗?在ExecuteAsync()函数中?我的意思是项目现在工作得很好。我看到服务器在 Jmeter 板(api应用程序)中注册成功。奇怪的是,我只有一个空的工作者类。
任何建议都很感激。

2guxujil

2guxujil1#

不需要空的worker类,只需调用AddHangfireServer就可以创建一个worker。
如果打开AddHangfireServer的源代码,您实际上可以看到服务器已注册:

public static IServiceCollection AddHangfireServer(
  [NotNull] this IServiceCollection services,
  [NotNull] Action<IServiceProvider, BackgroundJobServerOptions> optionsAction)
{
  if (services == null)
    throw new ArgumentNullException(nameof (services));
  return optionsAction != null ? HangfireServiceCollectionExtensions.AddHangfireServerInner(services, (JobStorage) null, (IEnumerable<IBackgroundProcess>) null, optionsAction) : throw new ArgumentNullException(nameof (optionsAction));
}

...

private static IServiceCollection AddHangfireServerInner(
  [NotNull] IServiceCollection services,
  [CanBeNull] JobStorage storage,
  [CanBeNull] IEnumerable<IBackgroundProcess> additionalProcesses,
  [NotNull] Action<IServiceProvider, BackgroundJobServerOptions> optionsAction)
{
  services.AddTransient<IHostedService, BackgroundJobServerHostedService>((Func<IServiceProvider, BackgroundJobServerHostedService>) (provider =>
  {
    BackgroundJobServerOptions options = new BackgroundJobServerOptions();
    optionsAction(provider, options);
    return HangfireServiceCollectionExtensions.CreateBackgroundJobServerHostedService(provider, storage, additionalProcesses, options);
  }));
  return services;
}

...

private static BackgroundJobServerHostedService CreateBackgroundJobServerHostedService(
  IServiceProvider provider,
  JobStorage storage,
  IEnumerable<IBackgroundProcess> additionalProcesses,
  BackgroundJobServerOptions options)
{
  HangfireServiceCollectionExtensions.ThrowIfNotConfigured(provider);
  storage = storage ?? provider.GetService<JobStorage>() ?? JobStorage.Current;
  additionalProcesses = additionalProcesses ?? provider.GetServices<IBackgroundProcess>();
  options.Activator = options.Activator ?? provider.GetService<JobActivator>();
  options.FilterProvider = options.FilterProvider ?? provider.GetService<IJobFilterProvider>();
  options.TimeZoneResolver = options.TimeZoneResolver ?? provider.GetService<ITimeZoneResolver>();
  IBackgroundJobFactory factory;
  IBackgroundJobStateChanger stateChanger;
  IBackgroundJobPerformer performer;
  HangfireServiceCollectionExtensions.GetInternalServices(provider, out factory, out stateChanger, out performer);
  IHostApplicationLifetime service = provider.GetService<IHostApplicationLifetime>();
  return new BackgroundJobServerHostedService(storage, options, additionalProcesses, factory, performer, stateChanger, service);
}

...

public class BackgroundJobServerHostedService : IHostedService, IDisposable
{

AddHangfireServer将简单地注册一个BackgroundJobServerHostedService,它将处理所有事情。
解决这类问题的一个好策略是在某个时候深入实际的源代码。
这在Hangfire.AspNetCore包的github上也有记录:

在Web应用程序中处理后台任务...

您可以在任何OWIN-compatible应用程序框架中处理后台任务,包括ASP.NET MVC、ASP.NET Web API、FubuMvc、Nancy等。忘记AppDomain卸载、Web Garden和Web Farm问题- Hangfire对于Web应用程序从头开始是可靠的,即使是在共享主机上。
app.UseHangfireServer();...或其他任何位置
在控制台应用程序、Windows服务、Azure工作者角色等中

using (new BackgroundJobServer())
{
    Console.WriteLine("Hangfire Server started. Press ENTER to exit...");
    Console.ReadLine();
}

https://github.com/HangfireIO/Hangfire

相关问题