1. 程式人生 > 實用技巧 >.NET之Hangfire快速入門和使用

.NET之Hangfire快速入門和使用

什麼是Hangfire

Hangfire是一個開源且商業免費使用的工具函式庫。可以讓你非常容易地在ASP.NET應用(也可以不在ASP.NET應用)中執行多種型別的後臺任務,而無需自行定製開發和管理基於Windows Service後臺任務執行器。且任務資訊可以被持久儲存。內建提供整合化的控制檯。

Hangfire的基本特徵與優點

與quartz.net對比

很大的原因在於專案需要一個後臺可監控的應用,不用每次都要從伺服器拉取日誌檢視,在沒有ELK的時候相當不方便。Hangfire控制面板不僅提供監控,也可以手動的觸發執行定時任務

HangFire例子

1、新建專案

2、引用安裝

這裡我們選擇redis 作為持久化方式,所以引用

Hangfire.Redis.StackExchange.StrongName

配置面板、redis連結

Startup.cs

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews().AddControllersAsServices();
            GlobalStateHandlers.Handlers.Add(new SucceededStateExpireHandler(int
.Parse(Configuration["Hangfire:JobExpirationTimeout"]))); services.AddHostedService<RecurringJobsService>(); services.AddHangfire(x => { var connectionString = Configuration["Hangfire:Redis:ConnectionString"]; x.UseRedisStorage(connectionString,
new RedisStorageOptions() { //活動伺服器超時時間 InvisibilityTimeout = TimeSpan.FromMinutes(60), Db = int.Parse(Configuration["Hangfire:Redis:Db"]) }); x.UseDashboardMetric(DashboardMetrics.ServerCount) .UseDashboardMetric(DashboardMetrics.RecurringJobCount) .UseDashboardMetric(DashboardMetrics.RetriesCount) .UseDashboardMetric(DashboardMetrics.AwaitingCount) .UseDashboardMetric(DashboardMetrics.EnqueuedAndQueueCount) .UseDashboardMetric(DashboardMetrics.ScheduledCount) .UseDashboardMetric(DashboardMetrics.ProcessingCount) .UseDashboardMetric(DashboardMetrics.SucceededCount) .UseDashboardMetric(DashboardMetrics.FailedCount) .UseDashboardMetric(DashboardMetrics.EnqueuedCountOrNull) .UseDashboardMetric(DashboardMetrics.FailedCountOrNull) .UseDashboardMetric(DashboardMetrics.DeletedCount); }); }

配置面板登入許可權、作業通道

 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();
            app.UseAuthorization();
            var filter = new BasicAuthAuthorizationFilter(
        new BasicAuthAuthorizationFilterOptions
        {
            SslRedirect = false,
            RequireSsl = false,
            LoginCaseSensitive = false,
            Users = new[]
            {
                        new BasicAuthAuthorizationUser
                        {
                            Login = Configuration["Hangfire:Login"] ,
                            PasswordClear= Configuration["Hangfire:PasswordClear"]
                        }
            }
        });
            app.UseHangfireDashboard("", new DashboardOptions
            {
                Authorization = new[]
                {
                   filter
                },
            });
            var jobOptions = new BackgroundJobServerOptions
            {
                Queues = new[] { "critical", "test", "default" },
                WorkerCount = Environment.ProcessorCount * int.Parse(Configuration["Hangfire:ProcessorCount"]),
                ServerName = Configuration["Hangfire:ServerName"],
                SchedulePollingInterval = TimeSpan.FromSeconds(1), //計劃輪詢間隔  支援任務到秒
            };
            app.UseHangfireServer(jobOptions);
        }

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "Hangfire": {
    "Redis": {
      "ConnectionString": "localhost:6379,password=123456,abortConnect=false",
      "Db": 10
    },
    "Login": "admin", //賬號
    "PasswordClear": "123456", //密碼
    "ServerName": "hangfire001", //站點服務名稱
    "JobExpirationTimeout": 1, //成功job過期時間 分鐘
    "ProcessorCount": 5 //執行緒數
  },
  "AllowedHosts": "*"
}

使用作業

using Hangfire;
using Hangfire.Server;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using TestService;

namespace hangfiretest.RecurringJobs
{
    internal class RecurringJobsService : BackgroundService
    {
        private readonly IBackgroundJobClient _backgroundJobs;
        private readonly IRecurringJobManager _recurringJobs;
        private readonly ILogger<RecurringJobScheduler> _logger;
        public Itest _test { get; set; }

        public RecurringJobsService(
            [NotNull] IBackgroundJobClient backgroundJobs,
            [NotNull] IRecurringJobManager recurringJobs,
            [NotNull] ILogger<RecurringJobScheduler> logger)
        {
            _backgroundJobs = backgroundJobs ?? throw new ArgumentNullException(nameof(backgroundJobs));
            _recurringJobs = recurringJobs ?? throw new ArgumentNullException(nameof(recurringJobs));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            try
            {
                _recurringJobs.AddOrUpdate<Itest>("seconds", i => _test.demo(), "*/1 * * * * *", queue: "critical");
                //_backgroundJobs.Enqueue<Services>(x => x.LongRunning(JobCancellationToken.Null));
                //_recurringJobs.AddOrUpdate("seconds", () => Console.WriteLine("Hello, seconds!"), "*/15 * * * * *");
                //_recurringJobs.AddOrUpdate("minutely", () => Console.WriteLine("Hello, world!"), Cron.Minutely);
                //_recurringJobs.AddOrUpdate("hourly", () => Console.WriteLine("Hello"), "25 15 * * *");
                //_recurringJobs.AddOrUpdate("neverfires", () => Console.WriteLine("Can only be triggered"), "0 0 31 2 *");
                //_recurringJobs.AddOrUpdate("Hawaiian", () => Console.WriteLine("Hawaiian"), "15 08 * * *", TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time"));
                //_recurringJobs.AddOrUpdate("UTC", () => Console.WriteLine("UTC"), "15 18 * * *");
                //_recurringJobs.AddOrUpdate("Russian", () => Console.WriteLine("Russian"), "15 21 * * *", TimeZoneInfo.Local);
            }
            catch (Exception e)
            {
                _logger.LogError("An exception occurred while creating recurring jobs.", e);
            }

            return Task.CompletedTask;
        }
    }
}

已完成的job設定過期,防止資料無限增長

using Hangfire.States;
using Hangfire.Storage;
using System;

namespace hangfiretest
{
    /// <summary>
    /// 已完成的job設定過期,防止資料無限增長
    /// </summary>
    public class SucceededStateExpireHandler : IStateHandler
    {
        public TimeSpan JobExpirationTimeout;

        public SucceededStateExpireHandler(int jobExpirationTimeout)
        {
            JobExpirationTimeout = TimeSpan.FromMinutes(jobExpirationTimeout);
        }

        public string StateName => SucceededState.StateName;

        public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
            context.JobExpirationTimeout = JobExpirationTimeout;
        }

        public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)
        {
        }
    }
}

RUN起來

是不是很簡單啊,以前我是使用quartz,自從使用了hangfire,反正我是再也不想在使用quartz;有點類似git和svn的關係;

完整程式碼:

https://github.com/conanl5566/mydemo/tree/master/hangfire/hangfire.demo