.NET Core 3.1 + Hangfire 配置以及踩坑


起初在基於ABP開發的個人博客中嘗試過使用Hangfire構建后台任務服務,期間配置相對簡單,畢竟ABP做了相應的拓展。現在常規的.NET Core 3.1框架下進行集成使用,並且是基於MySql 5.6,並對遇到的問題進行一個匯總。

集成Hangfire

構建完成后整個系統的結構:

添加后台任務層

1、在后台任務層中添加Hangfire Nuget 包

1、Hangfire.AspNetCore
2、Hangfire.Core
3、Hangfire.Dashboard.BasicAuthorization
4、Hangfire.MySql.Core

如上圖所示,添加一個以BackgroundJobs結尾的程序集,進行對后台任務接口、實現的分離。

其中主要有任務接口、對應實現以及常用的Cron定義

2、任務接口:IBackgroundJob

 public interface IBackgroundJob
    {
        /// <summary>
        /// 執行任務
        /// </summary>
        /// <returns></returns>
        Task ExecuteAsync();
    }

3、任務實現:HangfireTestJob

 public class HangfireTestJob : IBackgroundJob
    {
        public async Task ExecuteAsync()
        {
            Console.WriteLine("定時任務測試");
            await Task.CompletedTask;
        }
    }

4、常用的Cron:JobCronType

主要定義一些常用的Cron,靜態方法返回Cron 字符串

5、將任務實現注入到Autofac容器中

 public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterAssemblyTypes(Assembly.LoadFrom(backgroundJob))//后台任務
                   .Where(a=>a.IsClass )
                   .InstancePerDependency();
        }

服務注冊與添加中間件

1、配置Hang服務、中間件拓展

這塊采用添加拓展服務的形式添加Hangfire服務,讓Startup更簡潔,代碼如下:

public static void AddHangfireService(this IServiceCollection services )
        {
            services.AddHangfire(options =>
            {
                options.UseStorage(
                    new MySqlStorage(AppSettings.ConnectionString,//配置連接字符串,連接字符串需要加入Allow User Variables=true;配置
                    new MySqlStorageOptions
                    {
                        TablePrefix = "ps_hangfire"//配置表名前綴
                        }));
            });
        }

采用擴展的形式配置Hangfire中間件:

 public static void UseHangfireMiddleware(this IApplicationBuilder app, ILifetimeScope lifetimeScope)
        {
            app.UseHangfireServer();//添加hangfire服務中間件
            app.UseHangfireDashboard(options: new DashboardOptions
            {
                Authorization = new[] {new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
                {
                    RequireSsl = false,//需要SSL連接才能訪問HangFire Dahsboard。
                    SslRedirect = false,//是否將所有非SSL請求重定向到SSL URL
                    LoginCaseSensitive = true,//區分大小寫
                    Users = new []//用戶
                    {
                        new BasicAuthAuthorizationUser
                        {
                            Login = AppSettings.Hangfire.Login,
                            PasswordClear = AppSettings.Hangfire.Password
                        },
                    }
                }), },
                DashboardTitle = "任務調度中心"
            });//添加hangfire儀表盤中間件,添加登陸認證

            HangfireService(lifetimeScope);//配置各個任務
        }

        private static void HangfireService(ILifetimeScope lifetimeScope)
        {
            var job = lifetimeScope.Resolve<HangfireTestJob>();//獲取容器實例(記得的要注入任務實現)
            RecurringJob.AddOrUpdate("定時任務測試", () => job.ExecuteAsync(), JobCronType.Minute());
        }

2、添加服務、中間件

 public void ConfigureServices(IServiceCollection services)
        {
             #region Hangfire注冊

            services.AddHangfireService();

            #endregion
        }

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env ,ILifetimeScope lifetimeScope)
        {
     		app.UseHangfireMiddleware(lifetimeScope);//添加Hangfire中間件
 		}

出現的問題

問題有2個,一個是對於連接數據庫MySql5.6引發的問題,另一個是問題是個人對Autofac依賴注入的不了解導致的。

問題(一):在配合MySql5.6使用時,運行報錯

問題分析:在配置完成后,啟動項目,Hangfire在創建所需要的表時失敗,提示“Index column size too large. The maximum column size is 767 bytes.”,再次啟動時,會發現再次報錯為缺少名為“前綴_set ”的表。從錯誤信息中可看出,是INNODB 引擎,UTF-8,主鍵字符串 默認最大 767,所以導致報錯,進而導致生成的表不完全。

解決方案:

首先需要對數據庫進行如下設置:

SET GLOBAL INNODB_LARGE_PREFIX = ON;
SET GLOBAL innodb_file_format = BARRACUDA;

並進行查看是否生效:

SHOW variables like 'innodb_large_prefix';
SHOW variables like 'innodb_file_format';

最后,需要手動的創建者幾個缺少的表([前綴]_set、[前綴]_State、[前綴]_Job):

注意修改為自己的前綴

CREATE TABLE `ps_hangfire_Set` (
 `Id` int(11) NOT NULL AUTO_INCREMENT,
 `Key` varchar(100) NOT NULL,
 `Value` varchar(256) NOT NULL,
 `Score` float NOT NULL,
 `ExpireAt` datetime DEFAULT NULL,
 PRIMARY KEY (`Id`),
 UNIQUE KEY `IX_Set_Key_Value` (`Key`,`Value`)
) ENGINE=InnoDB  CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

CREATE TABLE `ps_hangfire_State`
(
	Id int(11) NOT NULL AUTO_INCREMENT,
	JobId int(11) NOT NULL,
	Name varchar(20) NOT NULL,
	Reason varchar(100) NULL,
	CreatedAt datetime NOT NULL,
	Data longtext NULL,
	PRIMARY KEY (`Id`),
	KEY `FK_HangFire_State_Job` (`JobId`)
) ENGINE=InnoDB  CHARSET=utf8mb4;

CREATE TABLE `ps_hangfire_List`
(
	`Id` int(11) NOT NULL AUTO_INCREMENT,
	`Key` varchar(100) NOT NULL,
	`Value` longtext NULL,
	`ExpireAt` datetime NULL,
	PRIMARY KEY (`Id`)
) ENGINE=InnoDB  CHARSET=utf8mb4;

問題(二):Autofac注冊任務后無法獲取到實例

問題分析:在Autofac注入時,我采用了程序集注入的形式注入,如下:

var backgroundJob = Path.Combine(basePath,"PaymentStatistics.BackgroundJobs.dll");
builder.RegisterAssemblyTypes(Assembly.LoadFrom(backgroundJob))//后台任務
                  .AsImplementedInterfaces()
                  .InstancePerDependency();

結果通過以下方式愣是拿不到實例:

var job = lifetimeScope.Resolve<HangfireTestJob>();

最后才發現,自己采用的是接口注冊的方式進行注冊,用實現類根本拿不到,只用用對應接口才能夠拿到:

var job = lifetimeScope.Resolve<IBackgroundJob>();

但是,這么拿也不對,畢竟我是一個接口,對應多個實例,這樣只能拿一個。

解決方案:

采用實例的注冊方式,這樣就能通過實現類去獲取實例:

var backgroundJob = Path.Combine(basePath,"PaymentStatistics.BackgroundJobs.dll");
builder.RegisterAssemblyTypes(Assembly.LoadFrom(backgroundJob))//后台任務
                  .Where(a=>a.IsClass )
                  .InstancePerDependency();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM