我一直在寻找使用 HangFire 作为 .NET 6 的 Windows 服务,官方文档已经有 10 年历史了。其他示例未指定如何设置工作人员服务。不管怎样,这是我的环境——我有一个带有 api 应用程序的 Web 应用程序。 api 应用程序是后台作业将在 HangFire 中排队的地方,但我希望实际处理位于不同的服务器上,例如应用程序服务器。所以我的目标是创建一个 Windows 服务来简单地运行 HangFire 服务器并继续让 api 应用程序来管理作业创建。
我创建了一个新的 Worker Service 项目,这是我的代码:
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);
}
}
所以我的问题是,我需要在主 worker 类(Class)中做任何事情吗?在 ExecuteAsync() 函数中?我的意思是该项目现在运行得很好。我看到服务器在仪表板(api 应用程序)中注册成功。我只有一个空的 worker 类(Class),这似乎很奇怪。
如有任何建议,我们将不胜感激。
最佳答案
你不需要那个空的 worker 类(Class)。只需调用 AddHangfireServer
即可处理创建工作线程。
打开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 上有记录:
Process background tasks inside a web application…
You can process background tasks in any OWIN-compatible application framework, including ASP.NET MVC, ASP.NET Web API, FubuMvc, Nancy, etc. Forget about AppDomain unloads, Web Garden & Web Farm issues – Hangfire is reliable for web applications from scratch, even on shared hosting.
app.UseHangfireServer();
… or anywhere elseIn console applications, Windows Service, Azure Worker Role, etc.
using (new BackgroundJobServer()) { Console.WriteLine("Hangfire Server started. Press ENTER to exit..."); Console.ReadLine(); }
关于.net - HangFire 作为 .NET 6 的 Windows 服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75211243/