什么是Hangfire
1.是分布式的后端作業調度框架,我們只需要關心業務邏輯代碼,而不用關心調度機制持。
2.官方原文:在.NET和.NET Core應用程序中執行后台處理的簡單方法。無需Windows服務或單獨的進程。免費開源且可用於商業應用。Easy to set up, easy to use。
3.Hangfire支持 多種持久化方式 , 存儲方式可支持sqlserver、redis,mongodb等等。
4.Hangfire支持所有類型的后台任務 - 短時間運行和長時間運行,CPU密集型和I/O密集型,一次性和周期性。
Hangfire組件的基本結構
核心組件:客戶端,服務端,持久化存儲。這些組件既可以分開部署在不同項目中也可以都部署在一個項目中。
客戶端: 創建任務–>1、配置HangFire數據庫連接 2、創建任務(在創建任務的時候HangFire會自動將任務序列化並存儲到數據)。
服務端: 執行任務–>1、配置HangFire數據庫連接 2、從HangFire數據庫系統表讀取客戶端創建的任務然后開線程並行執行,任務之間不沖突。(服務端可宿主在Windows服務、控制台程序、IIS中…)。
數據庫(持久化存儲): HangFire程序框架表–>創建任務的時候HangFire會自動生成無需關心,但要注意如果采用現有的數據庫,必須保證數據庫中沒有重名的表(aggregatedcounter、counter、distributedlock、hash、job、jobparameter、jobqueue、jobstate、list、server、set、state),數據庫可采用 MySQL ,MSSQL,Redis等根據實際的需求來
儀表盤(管理面板): 展示作業列表執行狀態和結果等相關信息–>在.Net WebForm或.Net MVC 或.NetCore MVC網站程序對接
HangFire 自從1.6+版本后開始支持NetCore。 1.6.17-1.6.22之間基本為對Hangfire.Core的升級。
創建HangFireDemo
1.創建個應用程序
2.引用3個Nuguet包
Hangfire.AspNetCore --Hangfire的組件的包
Hangfire.MySql.Core / Hangfire.Sqlserver --Hangfire的持久化數據庫的包
--權限包分為2種 任選其一 依賴項不同請自行選擇
Hangfire.Dashboard.Authorization --Hangfire的權限控制包 (依賴項是owin和hangfire.Core) (目前沒找到可視化界面的方法)
Hangfire.Dashboard.BasicAuthorization --Hangfire被人二次封裝的包。支持可視化界面的植入。支持core等
3.在管道中注入服務 (在Startup.cs 的ConfigureServices)
-
//hangfire的任務需要數據庫持久化
-
//Hangfire.AspNetCore
-
//Hangfire.MySql.Core mysql引用 大小寫敏感
-
//Hangfire.SqlServer sqlserver引用 大小寫敏感
-
-
//hangfire必須需要綁定一個持久化的連接數據。 官方推薦的是sqlserver,還有mg,mssql,pgsql,redis都是個人封裝的
-
//連接字符串必須加 Allow User Variables=true
-
services.AddHangfire(x => x.UseStorage( new MySqlStorage(
-
Configuration[ "ConnectionString"] ,
-
new MySqlStorageOptions
-
{
-
TransactionIsolationLevel = IsolationLevel.ReadCommitted, // 事務隔離級別。默認是讀取已提交。
-
QueuePollInterval = TimeSpan.FromSeconds( 15), //- 作業隊列輪詢間隔。默認值為15秒。
-
JobExpirationCheckInterval = TimeSpan.FromHours( 1), //- 作業到期檢查間隔(管理過期記錄)。默認值為1小時。
-
CountersAggregateInterval = TimeSpan.FromMinutes( 5), //- 聚合計數器的間隔。默認為5分鍾。
-
PrepareSchemaIfNecessary = true, //- 如果設置為true,則創建數據庫表。默認是true。
-
DashboardJobListLimit = 50000, //- 儀表板作業列表限制。默認值為50000。
-
TransactionTimeout = TimeSpan.FromMinutes( 1), //- 交易超時。默認為1分鍾。
-
TablePrefix = "Hangfire" //- 數據庫中表的前綴。默認為none
-
}
-
)));
ConnectionString是自己的連接字符串。放在配置文件中讀取。連接字符串中必須加上 Allow User Variables=true
MySqlStorage 提供了多個重載方法。可以只注入連接字符串,不添加連接的特性配置。
其中 IsolationLevel 引用的包是 using System.Data;
4.在管道后端使用
主要是使用2個服務
-
app.UseHangfireDashboard(); //使用hangfire面板
-
app.UseHangfireServer(); //啟動hangfire服務
4.1)自定義配置hangfire面板 (在Startup.cs 的Configure)
原生的面板如果不指定路徑。默認的進入面板的地址是 (端口號:/hangfire),支持自定義進入hangfire的路徑
Hangfire.Dashboard.Authorization 包的自定義方法 (方法1)
-
-
//添加面板的打開權限。不是所有人都可以打開面板。可以操作后台任務。
-
app.UseHangfireDashboard( "", new DashboardOptions
-
{
-
//方法1
-
Authorization = new[]
-
{
-
new BasicAuthAuthorizationFilterOptions
-
{
-
SslRedirect = false, // 是否將所有非SSL請求重定向到SSL URL
-
RequireSsl = false, // 需要SSL連接才能訪問HangFire Dahsboard。強烈建議在使用基本身份驗證時使用SSL
-
LoginCaseSensitive = false, //登錄檢查是否區分大小寫
-
Users = new[]
-
{
-
new BasicAuthAuthorizationUser
-
{
-
Login = "ycz",//用戶名
-
PasswordClear= "123456"
-
// Password as SHA1 hash
-
//Password=new byte[]{ 0xf3,0xfa,,0xd1 }
-
}
-
}
-
}
-
},
-
//方法2
-
Authorization = new[]
-
{
-
new HangfireAuthorizationFilter(){}
-
},
-
-
});
--下圖為 方法 2中啟用的自定義規則類 。需要重寫 IDashboardAuthorizationFilter 的 Authorize 方法
-
//默認是只能內網訪問。 需要重寫這個方法。注入規則
-
public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
-
{
-
-
//這里寫自定義規則
-
public bool Authorize([NotNull] DashboardContext context)
-
{
-
if (context.Request.LocalIpAddress.Equals("127.0.0.1") || context.Request.LocalIpAddress.Equals("::1"))
-
return true;
-
else
-
return false;
-
}
-
}
方法 1 是支持可視化界面的。 方法 2沒有可視化界面但是開放了接口 IDashboardAuthorizationFilter 可以重寫自定義規則
方法 1 和 方法 2 引用的包不一樣,不可同時使用兩種方法。需要注意
Hangfire.Dashboard.BasicAuthorization (方法1的包) Hangfire.Dashboard.Authorization (方法2的包)
而造成2種方法的差異性的就是。
啟用面板時的 Authorization 對象在2個包中所支持的類不同,方法1 的包被進行了二次封裝。
--這個是 方法1 的Authorization 對象所支持的類型
--這個是 方法2 的Authorization 對象所支持的類型
方法 1 和 方法 2 根據自己的需求。自行選擇。
方法1 可視化界面 輸入正確進入儀表盤。 輸入錯誤會刷新界面 繼續在當前登錄頁。
方法2 攔截器驗證 通過驗證直接進入儀表盤。 沒通過驗證返回 401
4.2)自定義配置hangfire服務
-
//配置要處理的隊列列表 ,如果多個服務器同時連接到數據庫,會認為是分布式的一份子。可以通過這個配置根據服務器性能的按比例分配請求,不會導致服務器的壓力。不配置則平分請求
-
var jobOptions = new BackgroundJobServerOptions
-
{
-
Queues = new[] { "test", "default" },//隊列名稱,只能為小寫
-
WorkerCount = Environment.ProcessorCount * 5, //並發任務數 --超出並發數。將等待之前任務的完成 (推薦並發線程是cpu 的 5倍)
-
ServerName = "hangfire1",//服務器名稱
-
};
-
-
app.UseHangfireServer(jobOptions); //啟動hangfire服務
hangfire的服務是可以自定義任務的分發的。因為框架是支持分布式,可以根據任務量根據服務器的性能選擇性的根據優劣比重進行分配任務,不然默認是平分任務。
5.配置完成
起服務。 ip:端口號/hangfire (沒自定義面板配置是hangfire,不然是自己定義的路由)進入可視化面板
同時你的數據庫里會創建12張表持久化你的任務
6.添加任務
hangfire的任務分為多種
隊列任務
基於隊列的任務處理是Hangfire中最常用的,客戶端使用BackgroundJob類的靜態方法Enqueue來調用,傳入指定的方法(或是匿名函數),Job Queue等參數,會立即添加到隊列中執行.
在任務被持久化到數據庫之后,Hangfire服務端立即從數據庫獲取相關任務並裝載到相應的Job Queue下,在沒有異常的情況下僅處理一次,
若發生異常,提供重試機制,異常及重試信息都會被記錄到數據庫中,通過Hangfire控制面板可以查看到這些信息。
任務執行不是同步的而是放到一個持久化隊列中,然后將請求控制權返回給調用者,不會堵塞
BackgroundJob.Enqueue(() => Console.WriteLine($@"隊列任務"));
延時任務
延遲(計划)任務跟隊列任務相似,不是立即調用而是設定一個未來時間點再來執行。最小的延時類型為秒級別的
BackgroundJob.Schedule(() => A(), TimeSpan.FromMinutes(1));
定時任務
定時循環任務執行:一行代碼添加重復執行的任務,其內置了常見的時間循環模式,也可基於CRON表達式來設定復雜的模式
第一種 時間格式 (萬能時間匹配)
* 52 15 12 4 *
這種字符串就是corn表達式 格式是 {秒數} {分鍾} {小時} {日期} {月份} {星期} {年份(可為空)} 空格間隔
*代表的是通配 任意 ?是只有在星期的時候可以占位的符號 代表這個時間不在意的,不會影響匹配
* 52 15 12 4 ?
第二種 時間格式 (循環時間匹配)
Corn.Minutely 是定時任務最小的循環單位,代表每分鍾執行一次。還支持的 年 月 日 小時的循環執行
第三種 時間格式 (自定義時間)
Cron.Daily(18,36) 支持多種重載方法 第一個參數是小時,第二個參數是分鍾。
(這種方法的本質其實也是底層使用的Cron表達式 ,不過這里只有5位/6位和cron定義的最多7位有差異。應該是省略了年)
如果是要指定的時間執行要注意時區問題。默認的是UTC的時區。想要使用當前時間則需要指定時間類型 TimeZoneInfo.Local
-
RecurringJob.AddOrUpdate("1",() => A(), "* 52 15 12 4 *"); //每年的4月12號15點52分任意秒執行 沒加時區 小時+8
-
RecurringJob.AddOrUpdate("2",() => A(), Cron.Minutely); //每分鍾執行一次。
-
RecurringJob.AddOrUpdate("3",() => A(), Cron.Daily(18,36),TimeZoneInfo.Local); //每天18點36分 當前時區
延續性任務執行
延續性任務類似於.NET中的Task,可以在第一個任務執行完之后緊接着再次執行另外的任務:
BackgroundJob.ContinueWith("1", () => B());
延續性批處理 和 批處理
pro版本 (需要商業授權和收費) 這里實現的效果應該和上面雷同 (這里沒有自己具體測試下面的功能)
-
//延續性批處理
-
BatchJob.StartNew(x =>
-
{
-
x.Enqueue(() => Console.WriteLine( "Job 1"));
-
x.Enqueue(() => Console.WriteLine( "Job 2"));
-
});
-
-
//批處理
-
BatchJob.ContinueWith(batchId, x =>
-
{
-
x.Enqueue(() => Console.WriteLine( "Last Job"));
-
});
里面的方法可以自定義多種類型我這里只是這三種了
個人總結:
1.只有定時任務和延續性任務支持自定義編號,如果不自己定義就會默認將方法名設置為編號 在可視化界面中
2.所有的記錄方法都會被持久化到數據庫里。如果關閉了服務。下次再開啟。之前的周期性任務依然會存在 依然會被執行
3.冗余規則(因為持久化的原因 服務的調用可能會重復執行定時任務,所以針對規則選擇定時任務的位置啟動在哪)
我定義了6個定時任務。不斷重復啟動,但是任務列表中只有這3個定時任務。說明規則冗余規則是: