Background job server is responsible for many tasks – execute jobs, schedule recurring and delayed jobs, perform storage maintenance, report heartbeats. For each such task dedicated server component is created that’s based on a dedicated thread.
It’s only necessary to have single full-fledged server for the whole storage instance, since only workers will and can act in parallel (for other components distributed lock is usually used). It is technically possible to use BackgroundProcessingServer instead of BackgroundJobServer (used by AddHangfireServer), but you will need to deconstruct the AddHangfireServer method. In this case no additional DelayedJobScheduler and RecurringJobScheduler components will be created, but still there will be additional storage-related components.
Or you can create only a single background job server and pass more workers as additional processes. In this case they will not be reflected on the Servers tab of the Dashboard UI, though. And also you’ll not be able to use AddHangfireServer, because in this case you’ll not be able to use the additionalProcess parameter.
But it’s possible to use the underlying BackgroundJobServerHostedService class:
var additionalProcesses = new List< IBackgroundProcess>();
additionalProcesses.Add(new Worker(new [] { "queue-1", "queue-2" }));
// add other workers
services.AddTransient<IHostedService>(provider => new BackgroundJobServerHostedService(
provider.GetService<JobStorage>(),
new BackgroundJobServerOptions { WorkerCount = 4, Queues = new [] { "default" } }), // some default queues
additionalProcesses);
In Hangfire 1.8 possibly there will be “lightweight” servers with only workers defined for cases like this and ability to show them in the Dashboard UI.
Great, thanks! By the way, since I am using dependency injection, creating workers like you specified did not work for me, I have implemented a method like the following, may I ask can you see any bad practise here?
Note: I am handling graceful shutdown myself.
public static IApplicationBuilder AddHangfireServer(
this IApplicationBuilder app,
BackgroundJobServerOptions options,
IEnumerable<string> additionalQueues)
{
var applicationServices = app.ApplicationServices;
var storage = applicationServices.GetRequiredService<JobStorage>();
options.Activator ??= applicationServices.GetService<JobActivator>();
options.FilterProvider ??= applicationServices.GetService<IJobFilterProvider>();
options.TimeZoneResolver ??= applicationServices.GetService<ITimeZoneResolver>();
var performer = new BackgroundJobPerformer(options.FilterProvider, options.Activator, TaskScheduler.Default);
var stateChanger = new BackgroundJobStateChanger(options.FilterProvider);
var workers = additionalQueues.Select(c => new Worker(new[] { c }, performer, stateChanger));
var server = new BackgroundJobServer(options, storage, workers);
Servers.Add(server);
return app;
}