.net core hangfire

与quartz.net对比

在项目没有引入Hangfire之前,一直使用的是Quartz.net。Quartz.net在定时任务处理方面优势如下:

  • 支持秒级单位的定时任务处理,但是Hangfire只能支持分钟及以上的定时任务处理

原因在于Hangfire用的是开源的NCrontab组件,跟linux上的crontab指令相似。

  • 更加复杂的触发器,日历以及任务调度处理

  • 可配置的定时任务

但是为什么要换Hangfire? 很大的原因在于项目需要一个后台可监控的应用,不用每次都要从服务器拉取日志查看,在没有ELK的时候相当不方便。Hangfire控制面板不仅提供监控,也可以手动的触发执行定时任务。如果在定时任务处理方面没有很高的要求,比如一定要5s定时执行,Hangfire值得拥有。抛开这些,Hangfire优势太明显了:

  • 持久化保存任务、队列、统计信息

  • 重试机制

  • 多语言支持

  • 支持任务取消

  • 支持按指定Job Queue处理任务

  • 服务器端工作线程可控,即job执行并发数控制

  • 分布式部署,支持高可用

  • 良好的扩展性,如支持IOC、Hangfire Dashboard授权控制、Asp.net Core、持久化存储等

这里演示在asp.net core使用Redis作为Hangfire的持久化存储

1、引用包

Hangfire.AspNetCore

Hangfire.Redis.StackExchange

2、注入hangfire及配置

这里说明下 services.AddHostedService<MyJob1>();是定时任务,继承自BackgroundService。

然后在configure中引入hangfire。如果在这里不引入队列的名字,会导致添加的任务无法加入到队列。

 app.UseHangfireDashboard();表示使用看板功能。

 3、普通使用,比如添加任务到队列或者添加延时任务,基本是一样的。

这里在接口中直接演示,直接使用BackgroundJob.Enqueue将方法添加到队列。

注意队列名称要使用小写,被添加到队列中的方法需要是Public的。

4、定时任务

定义定时任务配置类

 /// <summary>
    /// 工作任务配置
    /// </summary>
    public class WorkerConfig
    {
        /// <summary>
        /// 轮询秒数
        /// </summary>
        public int IntervalSecond { get; set; }
        /// <summary>
        /// 工作唯一编号
        /// </summary>
        public string WorkerId { get; set; }
        /// <summary>
        /// 队列名称
        /// </summary>
        public string QueuesName { get; set; }
        /// <summary>
        /// 表达式
        /// </summary>
        public string Cron { get; set; }
    }

定义需要定时执行的接口方法

/// <summary>
    /// 所有的后台工作者类都应实现该接口
    /// </summary>
    public interface IBackgroundWorkerDo
    {
        /// <summary>
        /// 执行具体的任务
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        Task DoWorkAsync(PerformContext context);
    }

定义hangfire基类,定时任务的类都继承该类。

 public abstract class HangfireWorkerPxoxy:BackgroundService
    {
        private readonly IConfiguration _appConfiguration;
        private readonly WorkerConfig _config;
        public HangfireWorkerPxoxy(IConfiguration appConfiguration, WorkerConfig Config)
        {
            _appConfiguration = appConfiguration;
            _config = Config;
        }
        
        public void Excete<T>() where T : IBackgroundWorkerDo
        {
            var appsetting = _appConfiguration.GetSection("Appsetting").Get<Appsetting>();
            var options = new RedisStorageOptions
            {
                Db = appsetting.RedisConfig.Defaultdatabase,
                SucceededListSize = 50000,
                DeletedListSize = 50000,
                ExpiryCheckInterval = TimeSpan.FromHours(1),
                InvisibilityTimeout = TimeSpan.FromHours(3)
            };
            var redisString = appsetting.RedisConfig.HostName + ":" + appsetting.RedisConfig.Port + ",defaultdatabase="+ appsetting.RedisConfig.Defaultdatabase;
            JobStorage.Current = new RedisStorage(redisString);
            var manager = new RecurringJobManager(JobStorage.Current);
            string workerId = _config.WorkerId;
            string cron = string.IsNullOrEmpty(_config.Cron) ? Cron.MinuteInterval(_config.IntervalSecond / 60) : _config.Cron;
            manager.RemoveIfExists(workerId);
            manager.AddOrUpdate(workerId, Job.FromExpression<T>((t) => t.DoWorkAsync(null)), cron, TimeZoneInfo.Local, string.IsNullOrEmpty(_config.QueuesName) ? "default" : _config.QueuesName);
        }

定义最终的定时任务类

  /// <summary>
    ///  定时任务
    /// </summary>
    class MyJob1 : HangfireWorkerPxoxy,IBackgroundWorkerDo
    {
        private readonly ILogger _logger;
      
        public MyJob1(IConfiguration _appConfiguration, ILogger<MyJob1> logger) : base(_appConfiguration, new WorkerConfig { IntervalSecond = 60 * 1, WorkerId = "RealWorker", QueuesName = "queue1" })
        {
            _logger = logger;
        }

        /// <summary>
        ///  添加同步sku库存任务
        /// </summary>
        public  async Task DoWorkAsync(PerformContext context)
        {
            var temp = await Task.FromResult(0);
            if (context == null)
            {
                return ;
            }
            _logger.LogInformation($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 开始添加同步数据任务 ...");
           
            Thread.Sleep(1000*10);
            _logger.LogInformation($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 结束添加同步数据任务 ...");
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var temp= await Task.FromResult(0);
            Excete<MyJob1>();
        }

        
    }

注入定时任务类,services.AddHostedService<MyJob1>();

最后看下效果。

ok,大功告成。

原文地址:https://www.cnblogs.com/KQNLL/p/11875171.html