marcoCasamento / Hangfire.Redis.StackExchange

HangFire Redis storage based on original (and now unsupported) Hangfire.Redis but using lovely StackExchange.Redis client
Other
460 stars 109 forks source link

Hangfire.Redis.StackExchange

HangFire Redis storage based on HangFire.Redis but using lovely StackExchange.Redis client.

Build status Nuget Badge

Package Name NuGet.org
Hangfire.Redis.StackExchange Nuget Badge
Hangfire.Redis.StackExchange.StrongName Nuget Badge

Highlights

Despite the name, Hangfire.Redis.StackExchange.StrongName is not signed because Hangfire.Core is not yet signed.

Tutorial: Hangfire on Redis on ASP.NET Core MVC

Getting Started

To use Hangfire against Redis in an ASP.NET Core MVC projects, first you will need to install at least these two packages: Hangfire.AspNetCore and Hangfire.Redis.StackExchange.

In Startup.cs, these are the bare minimum codes that you will need to write for enabling Hangfire on Redis:

public class Startup
{
    public static ConnectionMultiplexer Redis;

    public Startup(IHostingEnvironment env)
    {
        // Other codes / configurations are omitted for brevity.
        Redis = ConnectionMultiplexer.Connect(Configuration.GetConnectionString("Redis"));
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHangfire(configuration =>
        {
            configuration.UseRedisStorage(Redis);
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        app.UseHangfireServer();
    }
}

Attention: If you are using Microsoft.Extensions.Caching.Redis package, you will need to use Hangfire.Redis.StackExchange.StrongName instead, because the former package requires StackExchange.Redis.StrongName instead of StackExchange.Redis!

Configurations

configuration.UseRedisStorage(...)

This method accepts two parameters:

namespace Hangfire.Redis
{
    public class RedisStorageOptions
    {
        public const string DefaultPrefix = "{hangfire}:";

        public RedisStorageOptions();

        public TimeSpan InvisibilityTimeout { get; set; }
        public TimeSpan FetchTimeout { get; set; }
        public string Prefix { get; set; }
        public int Db { get; set; }
        public int SucceededListSize { get; set; }
        public int DeletedListSize { get; set; }
    }
}

It is highly recommended to set the Prefix property, to avoid overlap with other projects that targets the same Redis store!

app.UseHangfireServer(...)

This method accepts BackgroundJobServerOptions as the first parameter:

namespace Hangfire
{
    public class BackgroundJobServerOptions
    {
        public BackgroundJobServerOptions();

        public string ServerName { get; set; }
        public int WorkerCount { get; set; }
        public string[] Queues { get; set; }
        public TimeSpan ShutdownTimeout { get; set; }
        public TimeSpan SchedulePollingInterval { get; set; }
        public TimeSpan HeartbeatInterval { get; set; }
        public TimeSpan ServerTimeout { get; set; }
        public TimeSpan ServerCheckInterval { get; set; }
        public IJobFilterProvider FilterProvider { get; set; }
        public JobActivator Activator { get; set; }
    }
}

Of these options, several interval options may be manually set (to longer intervals) to reduce CPU load:

Dashboard

Written below is a short snippet on how to implement Hangfire dashboard in ASP.NET Core MVC applications, with limited access to cookie-authenticated users of Administrator role. Read more in official documentation.

public class AdministratorHangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter
{
    public bool Authorize(DashboardContext context)
    {
        var user = context.GetHttpContext().User;
        return user.Identity.IsAuthenticated && user.IsInRole("Administrator");
    }
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseCookieAuthentication(...);

    // This middleware must be placed AFTER the authentication middlewares!
    app.UseHangfireDashboard(options: new DashboardOptions
    {
        Authorization = new[] { new AdministratorHangfireDashboardAuthorizationFilter() }
    });
}

Jobs via ASP.NET Core Dependency Injection Services

For cleaner and more managable application code, it is possible to define your jobs in a class that is registered via dependency injection.

public class MyHangfireJobs
{
    public async Task SendGetRequest()
    {
        var client = new HttpClient();
        await client.GetAsync("https://www.accelist.com");
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<MyHangfireJobs>();
}

Using this technique, the registered jobs service will be able to obtain other services as dependency via constructor parameters, such as Entity Framework Core DbContext; which enables the development of powerful jobs with relative ease.

Then later you can execute the jobs using generic expression:

BackgroundJob.Enqueue<MyHangfireJobs>(jobs => jobs.SendGetRequest());

BackgroundJob.Schedule<MyHangfireJobs>(jobs => jobs.SendGetRequest(), DateTimeOffset.UtcNow.AddDays(1));

RecurringJob.AddOrUpdate<MyHangfireJobs>("RecurringSendGetRequest", jobs => jobs.SendGetRequest(), Cron.Hourly());