日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#-初识Hangfire

發布時間:2024/1/8 C# 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#-初识Hangfire 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Hangfire 分布式后端作業調度框架服務

    • 概述
    • 特點
    • 版本
    • 基本結構
    • 基本功能及使用
    • 基本功能的部分對象解析
    • 項目引入
    • 拓展
      • MySqlStorageOptions 數據庫配置項
      • BackgroundJobServerOptions 服務端配置項
      • DashBoard 頁面配置權限認證(登陸)
      • Hangfire的數據庫分析(MySql)
      • 使ASP.NET應用程序始終運行
    • 開發過程需要注意的點
    • 體驗得到的一些缺點/注意點
    • 留疑/思索

概述

分布式后端作業調度框架服務,我們只需要關心業務邏輯代碼,而不用關心調度機制持。

官方原文:在.NET和.NET Core應用程序中執行后臺處理的簡單方法。無需Windows服務或單獨的進程。免費開源且可用于商業應用。Easy to set up, easy to use。

Hangfire支持久存儲支,存儲方式可支持sqlserver、redis,mongodb等等。
Hangfire支持所有類型的后臺任務 - 短時間運行和長時間運行,CPU密集型和I/O密集型,一次性和周期性。

官網:www.hangfire.io

特點

簡單 Simple
易于設置易于使用。沒有windows服務,沒有windows計劃策劃稿內需,不需要單獨的應用程序。

后臺作業是常規靜態或帶有常規參數的實例.NET方法-不需要基類或者接口實現。

PS:啟動和結束依托于宿主項目。可以正常的調用項目內部的方法,無需特意實現某個類或接口。實施上,它會拿取到你的方法的引用來源并和參數一起保存下來,空間名方法名項目名等。

可靠 Reliable
一旦創建了后臺作業而沒有任何異常,Hangfire將負責至少處理它一次。

您可以自由的拋出未處理的異常或終止您的應用程序 - 后臺作業將會自動重新嘗試。

PS:只要可以正常放入計劃中,就一定能執行。當然,它沒提到能否精確執行。事實上很多情況下會導致重復執行(某一任務執行時間過長)或不執行(多任務時間要求過于緊湊)或未按時執行(如網站被回收后又重新激活,此時若錯過了執行時間,但是依舊會在激活第一時間執行)。

高效 Efficient
雖然默認安裝使用SQL Server和輪詢技術來獲取作業,但您可以利用MSMQ或Redis擴展將處理延遲降低至最低。

PS:很顯然是相對而言。
持久化 Persistent
后臺作業時在永久存儲創建-SQL服務器,Redis,PostgreSQL,MongoDB或其它。
您可以安全的重新啟動應用程序并在ASP.NET中使用Hangfire,而無需擔心應用程序池的循環使用。

PS:與RbaitMQ的消息持久化存儲類似,實際上只要想達到永久存儲肯定要有硬盤參與。
分布式 Distributed
后臺方法調用以及其參數被序列化可以克服過程邊界。
您可以在不同的計算機上使用hangfire以獲得更多處理能力而無需配置 - 自動執行同步。

PS:參數全部被序列化的。分布式可以用是可以用但是并不智能,也無法自己開發服務端平衡機制。簡單易用但也死板。
自我維護 Self-maintainable
您無需執行手動存儲清理 - hangfire會盡可能保持清潔并自動刪除舊紀錄。

PS:就是盡量少占用數據庫空間。而且對于異常信息的記錄也并不詳細。
透明 Transparent
內置的web睫毛允許您查看后臺處理的整個畫面,以及觀察每個后臺作業的狀態。

對流行的日志框架的開箱即用支持允許您在零配置的早期捕獲異常。

PS:有個簡單的可視窗口頁面。
擴展 Extensible
作業篩選器允許您以類似于ASP.NET MVC操作篩選器的方式向后臺處理添加自定義功能。

PS:應該很有用,但是目前尚未使用過。
開源 Open source
Hangfire是開源軟件,完全免費用于商業用途,它是根據LGPLv3許可證授權的。
GitHub。

版本

截止目前(2019-02-20)最新穩定版為1.6.22。
當前項目使用版本為1.6.17。
自從1.6+版本后開始支持NetCore。
1.6.17-1.6.22之間基本為對Hangfire.Core的升級。
官網:各版本列表

基本結構

核心組件:客戶端,服務端,持久化存儲。這些組件既可以分開部署在不同項目中也可以都部署在一個項目中。

客戶端: 創建任務–>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):

  • 方案一(不推薦):客戶端A、服務端B、儀表盤C 分別運行在3個獨立的項目中。
  • 優缺點:沒什么優點,而且AB分開會導致服務端B從數據庫提取任務列表,準備反向加載程序集執行任務的時候,在自己的程序代碼中找不到對應的業務邏輯代碼或者引用類,因為業務邏輯代碼是在客戶端A項目中創建,自然業務邏輯類及功能代碼都在A項目的程序集中,自然找不到。
  • 讓服務端B也引用YourOwnJobLibrary.dll即可,但是這并不是我們想要的結果,以后更新服務什么的還得兩頭更新很麻煩。
  • 方案二(推薦):客戶端A、服務端B在同一個項目中,儀表盤C獨立網站項目中。
  • 優缺點:這樣比較合理,更新服務方便,即便沒有儀表盤C,作業正常調度執行;適合后臺相對固定的作業。
  • 可以將AB放在Windows服務項目中(VS中可以創建),這樣系統重啟什么的毫無顧慮,更新服務程序的時候只需停止服務->替換程序->開啟服務即可。
  • 方案三(中等):客戶端A、服務端B、儀表盤C在同一網站項目只能是網站項目,因為儀表盤只能在Web項目(API、WebForm、MVC )中。
  • 優缺點:視情況也,如果宿主在IIS中,IIS默認20分鐘沒有人訪問會停止,HangFire作業服務也會定制,可將此時間在IIS配置中延長,比較適合任務經常需要靈活變動處理的場景。

備注(來自參考1):

  • 創建任務可以是在控制臺程序Main方法中執行一次把任務Load到數據庫,或則在網站上用戶點擊某個按鈕執行后端方法創建。
  • 服務端比較靈活,可宿主在Windows服務、控制臺程序、IIS中…
  • 首次啟動連接數據庫時,會自動生成12張系統表,請確保現有庫中不存在以下表名:aggregatedcounter、counter、distributedlock、hash、job、jobparameter、jobqueue、jobstate、list、server、set、state
  • 原則上同一臺電腦上只需要1個服務端存在,測試中發現這么一種情況:服務端B宿主在D控制臺程序中,因誤操作在儀表盤C網站項目中也啟動一個HangeFire宿主服務E,最后的結果是服務端B的程序失效,服務端E正常運作,但是儀表盤上顯示找不到任務作業對應的程序集,因為程序集在服務端B程序中,如方案1的System.IO.FileNotFoundException…
  • HangFire執行的任務里面如果涉及到C盤創建文件夾 可能會因為權限問題 創建失敗,采用管理員權限運行程序即可創建
  • 基本功能及使用

    使用:
    引用命名空間。

    using Hangfire;
    • Fire-and-forget jobs(基于隊列的任務處理)
    var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget!"));
    • Delayed jobs(延遲任務執行)
    var jobId = BackgroundJob.Schedule(() => Console.WriteLine("Delayed!"), TimeSpan.FromDays(7));
    • Recurring jobs(定時任務執行)
    RecurringJob.AddOrUpdate(() => Console.WriteLine("Recurring!"), Cron.Daily);
    • Continuations(延續性任務執行)
    BackgroundJob.ContinueWith(jobId , () => Console.WriteLine("Continuation!"));
    • Batches(批處理) | Pro 版
    var batchId = BatchJob.StartNew(x => {x.Enqueue(() => Console.WriteLine("Job 1"));x.Enqueue(() => Console.WriteLine("Job 2")); })
    • Batch Continuations(延續性批處理) | Pro 版
    BatchJob.ContinueWith(batchId, x => {x.Enqueue(() => Console.WriteLine("Last Job")); });

    基本功能的部分對象解析

    • RecurringJob.AddOrUpdate()
    // 內部源碼 public static void AddOrUpdate(Expression<Action> methodCall, string cronExpression, TimeZoneInfo timeZone = null, tring queue = EnqueuedState.DefaultQueue) {var job = Job.FromExpression(methodCall);var id = GetRecurringJobId(job);Instance.Value.AddOrUpdate(id, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue); }

    該方法用于定期作業在指定的CRON計劃上觸發多次。該方法具有16個重載。
    Job.FromExpression(methodCall) 用于獲取基于Job類的新實例給定的方法調用的表達式樹。
    GetRecurringJobId(job)方法根據Job對象獲取對應的JobID。

    我們常用這個方法來增加定時執行的任務,實際使用實例

    // 使用實例 RecurringJob.AddOrUpdate(() => remindService.PushNoSubmittedDailyReportMsg(), Cron.Daily(8), TimeZoneInfo.Local, bgOption.Queues[0]); // 每天早8點 RecurringJob.AddOrUpdate(() => remindService.PushNoSubmittedWeeklyReportMsg(), Cron.Weekly(DayOfWeek.Monday, 8), TimeZoneInfo.Local, bgOption.Queues[0]); // 每周周一早八點 RecurringJob.AddOrUpdate(() => remindService.PushNoSubmittedMonthlyReportMsg(), Cron.Monthly(1, 8), TimeZoneInfo.Local, bgOption.Queues[0]); // 每月1日早8點
    • BackgroundJob.Enqueue()

    請注意,該方法的名稱為’Enqueue’,而不是’Call’,'Invoke’等。選擇該方法的名稱是為了強調給定方法的調用僅在當前執行上下文中排隊,并且在排隊之后立即將控制返回給調用線程。
    它會在不同的執行上下文中調用。What does this mean?有些事情會脫離你對方法調用過程的通常期望。你應該知道他們。
    (上面兩段文字來自官網文檔中的一個文章連接,也許是來著開發者的善意提醒。這篇文章也是一篇有助于理解hangfire的文章。See More)

    // 內部源碼 public static string Enqueue([NotNull, InstantHandle] Expression<Action> methodCall) {var client = ClientFactory();return client.Enqueue(methodCall); }

    該方法基于給定的方法調用表達式創建一個新的fire-and-forget作業。該方法接受一個參數,表示將被編組到服務器的方法調用表達式。接下來我們看一下var client = ClientFactory();方法的具體實現
    內部源碼:

    // 內部源碼 internal static Func<IBackgroundJobClient> ClientFactory {get{lock (ClientFactoryLock){return _clientFactory ?? DefaultFactory;}}set{lock (ClientFactoryLock){_clientFactory = value;}} }

    該屬性定義了一個Func的泛型委托。該屬性是一個可讀可寫的操作,對ClientFactoryLock加鎖,確保不發生死鎖情況。

    我們常用這個方法來在后端服務中新增執行的任務,實際使用實例

    // 使用實例 var jobId = BackgroundJob.Enqueue(() => throwExTest(throwEx));
    • BackgroundJob.ContinueWith()
    // 內部源碼 public static string ContinueWith([NotNull] string parentId, [InstantHandle][NotNull] Expression<Action> methodCall);

    該方法用于新增一個延續性作業,即當傳入的作業id的作業執行完畢后,才會執行該作業。
    使用該方法要注意的是,其等待執行完畢的上一個作業,如果未成功執行完畢,因為拋異常等原因導致仍在計劃中、等待狀態的話,該作業就會一直處于等待狀態。只有當上一個作業成功執行完畢后,才會將該作業由等待狀態轉為計劃中,等待被執行。

    // 使用實例 var jobId1 = BackgroundJob.Enqueue(() => throwExTest(throwEx)); var jobId2= BackgroundJob.ContinueWith(jobId1, () => hangfireCheckTest(pushCircularJobId));

    項目引入

    我新建了一個空白的,ASP.NET Web + MVC空白項目,在該項目上進行的步驟。

  • NuGet下載安裝Hangfire;NuGet下載安裝Hangfire.MySql.Core;
  • 在項目的App_Start文件夾中的Startup.cs的Configuration中添加代碼
  • using Microsoft.Owin; using Owin; using System; using Hangfire; using Hangfire.MySql; // MySqlStorage using Hangfire.MySql.Core; using System.Data; // IsolationLevel using System.Web.Configuration; // ConnectionStrings using Hangfire.Dashboard;[assembly: OwinStartup(typeof(Web.Startup))] namespace Web {public class Startup{public void Configuration(IAppBuilder app){// 簡潔版本一 無任何自定義配置,均采用默認配置var dbHangfire = WebConfigurationManager.ConnectionStrings["dbHangfire"].ToString();GlobalConfiguration.Configuration.UseStorage(new MySqlStorage(dbHangfire));app.UseHangfireDashboard();app.UseHangfireServer();}} }
  • Web.config中添加數據庫連接串。
  • <connectionStrings> <add name="dbHangfire" connectionString="server=;initial catalog=;persist security info=True;user id=;password=;Character Set=utf8;Allow User Variables=True" providerName="MySql.Data.MySqlClient" /></connectionStrings>
  • 然后運行項目就可以跑起來了。默認的面板連接為/hangfire。
  • 演示結果如下:

    備注:

  • Startup.cs類如果是手動創建的,未執行,請檢查Microsoft.Owin.Host.SystemWeb包是否安裝,Web.config中是否有
    <add key="owin:AutomaticAppStartup" value="true" />。
  • 根據使用的是Hangfire.MySql還是hangfire.MySql.Core,或則高版本或低版本,在自定義配置項時會有所區別。比如MySqlStorage的初始化入參傳遞數據庫連接串信息時,根據情況會需要傳遞ConnectionStrings的name或必須得是ConnectionStrings。
  • 拓展

    MySqlStorageOptions 數據庫配置項

    // 配置 MySqlStorageOptions var dbHangfire = WebConfigurationManager.ConnectionStrings["dbHangfire"].ToString(); GlobalConfiguration.Configuration.UseStorage(new MySqlStorage(dbHangfire, new MySqlStorageOptions {TransactionIsolationLevel = IsolationLevel.ReadCommitted, // 事務隔離級別。默認值為讀提交。QueuePollInterval = TimeSpan.FromSeconds(15), // 作業隊列輪詢間隔。默認值為15秒JobExpirationCheckInterval = TimeSpan.FromHours(1), // 作業過期檢查間隔(管理過期記錄)。默認為1小時CountersAggregateInterval = TimeSpan.FromMinutes(5), // 間隔到聚合計數器。默認為5分鐘PrepareSchemaIfNecessary = true, // 如果設置為true,則創建數據庫表。默認值為trueDashboardJobListLimit = 50000, // 儀表板作業列表上限。默認值為50000 TransactionTimeout = TimeSpan.FromMinutes(1), // 事務超時。默認為1分鐘 })); app.UseHangfireDashboard(); app.UseHangfireServer();

    BackgroundJobServerOptions 服務端配置項

    // 配置 服務端配置項 var dbHangfire = WebConfigurationManager.ConnectionStrings["dbHangfire"].ToString(); GlobalConfiguration.Configuration.UseStorage(new MySqlStorage(dbHangfire)); app.UseHangfireDashboard(); var bgOption = new BackgroundJobServerOptions {ServerName = String.Format("{0}.{1}", Environment.MachineName, Guid.NewGuid().ToString()), //服務器唯一的標識符Queues = new string[] { "defult" }, // 自定義隊列WorkerCount = 5, //最大并行數SchedulePollingInterval = TimeSpan.FromMinutes(1), }; app.UseHangfireServer(bgOption);

    備注:

  • 每個Hangfire服務器都有一個唯一的標識符,由兩部分組成,后一部分是處理同一臺機器上多個服務器的進程ID。前一部分是服務器名稱,默認為計算機名稱,用于處理不同計算機的唯一性。由于默認值僅在進程級別上提供唯一性,因此如果要在同一進程中運行不同的服務器實例,那么你就需要如上所示進行手動處理,上面實例就是手動new NewGuid。
  • 對于Queues 如果開發者不自行配置的話,hangfire默認是只增加一個名為‘defult’的隊列,既意為若未進行配置,就一個隊列可選Queues[0]。
  • 隊列名稱參數必須僅包含小寫字母,數字和下劃線字符。
  • 要將作業放入不同的隊列,方法一是在創建作業時選擇隊列,二是在方法上使用QueueAttribute類。
  • // 方法一 如bgOption.Queues[0] 這樣指定隊列 RecurringJob.AddOrUpdate(() => remindService.CreateReport(), Cron.Daily(0), TimeZoneInfo.Local, bgOption.Queues[0]) //方法二 使用QueueAttribute類 [Queue("critical")] public void SomeMethod() { }BackgroundJob.Enqueue(() => SomeMethod())
  • hangfire將首先從關鍵隊列中獲取作業,然后從默認隊列中獲取作業
  • 關于WorkerCount ,有文章稱其默認值是CPU核心數*5,但可能是被官網資料的代碼示例誤導WorkerCount = Environment.ProcessorCount * 5,我的主機為I5-8400,6核心,Environment.ProcessorCount為6,默認值為20。不過因為未在多臺主機測試不確定是否與什么有關還是就是寫死的默認值為20。測試環境的機器配置為2核心,但是部署后其默認值為10。所以如果有并發要求最好自己手動配置數量。
  • DashBoard 頁面配置權限認證(登陸)

    概述:
    Hangfire Dashboard為我們提供了可視化的對后臺任務進行管理的界面,我們可以直接在這個頁面上對定時任務進行刪除、立即執行等操作。
    默認情況下,這個頁面只能在部署Hangfire的機器上進行訪問,想要在其他地方進行訪問,需要配置權限認證模塊:Hangfire.Dashboard.Authorization。
    引用安裝:

  • 項目地址:https://github.com/HangfireIO/Hangfire.Dashboard.Authorization
  • 在已經安裝Hangfire基本組件的項目中,通過Nuget程序包管理器添加Hangfire.Dashboard.Authorizaiton或Nuget控制臺添加。
    通過Nuget程序包管理控制臺安裝的命令:通過Nuget程序包管理控制臺安裝的命令:
    Install-Package Hangfire.Dashboard.Authorization
  • 應用:
    在Startup.cs中的Configuration方法中添加以下代碼:
    在代碼中的Login和Password后面寫登錄的用戶名和密碼,這樣在下次打開Hangfire的Dashboard時,就會彈出需要輸入用戶名和密碼的窗口了,輸入之后就可了打開Dashboard了。

    using Hangfire; using Hangfire.MySql; // MySqlStorage using Hangfire.MySql.Core; using System.Data; // IsolationLevel using System.Web.Configuration; // ConnectionStrings using Hangfire.Dashboard; // Startup.cs > Configuration // 配置 登陸var dbHangfire = WebConfigurationManager.ConnectionStrings["dbHangfire"].ToString();GlobalConfiguration.Configuration.UseStorage(new MySqlStorage(dbHangfire));var filter = new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions{SslRedirect = false, // 是否將所有非SSL請求重定向到SSL URLRequireSsl = false, // 需要SSL連接才能訪問HangFire Dahsboard。強烈建議在使用基本身份驗證時使用SSLLoginCaseSensitive = false, //登錄檢查是否區分大小寫Users = new[]{new BasicAuthAuthorizationUser{Login ="mzc",//用戶名PasswordClear="mzc"// Password as SHA1 hash//Password=new byte[]{ 0xf3,0xfa,,0xd1 }}}});var options = new DashboardOptions{//Authorization = new[] { new HangfireDashboardAuthorizationFilter() }AuthorizationFilters = new[] {filter}};app.UseHangfireDashboard("/hf", options);app.UseHangfireServer();

    SHA1 hash密碼生成方式(來自參考4):

    string password = "<your password here>"; using (var cryptoProvider = System.Security.Cryptography.SHA1.Create()) {byte[] passwordHash = cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(password));string result = "new byte[] { " + String.Join(",", passwordHash.Select(x => "0x" + x.ToString("x2")).ToArray())+ " } "; }

    實際應用結果:

    備注:

  • 注意的是,使用AuthorizationFilters ,會有過時將來被移除的提示。提示推薦使用Authorization = new[] { new HangfireDashboardAuthorizationFilter() }
  • 根據使用版本不同,其應用方法和對象均有所改動。此處我是用的是Hangfire.MySql.Core最新版本nugert包。使用Hangfire.MySql的1.0.0.0包我未能成功實現,雖然編譯未報錯,但是跳轉login頁面時報404,尚待研究。而且Hangfire.MySql的0.0.0.7版本的包甚至在配置數據庫配置項那一步就出了問題。
  • 這種應用是適用于本地打開,即hangfire默認獲取當前本地權限,在此基礎上添加了登陸驗證。若是后端服務器,想要被遠程登陸訪問,應該是另一種思路去實現。
  • Hangfire的數據庫分析(MySql)

    hangfire總共會在數據庫中創建十二張表,用以做數據永久化存儲。
    aggregatedcounter,counter,distributedlock,hash,job,jobparameter,jobqueue,jobstate,list,server,set,state。

    使ASP.NET應用程序始終運行

    一下內容為官網內容,只是官網為英文版,此處只是將翻譯后的中文放在這里,結尾會附官網連接:

    默認情況下,在第一個用戶訪問您的站點之前,不會啟動Web應用程序中的Hangfire Server實例。更重要的是,有些事件會在一段時間后將您的Web應用程序關閉(我說的是Idle Timeout和不同的應用程序池回收事件)。在這些情況下,您的重復任務和延遲的作業將不會排隊,并且不會處理排隊的作業。

    • 步驟一:內部部署應用程序

    對于在您控制的服務器上運行的Web應用程序(物理或虛擬),您可以使用Windows?≥2002R2附帶的IIS≥7.5的自動啟動功能。完整設置需要執行以下步驟:

  • 啟用Windows進程激活(WAS)和萬維網發布(W3SVC)服務的自動啟動(默認情況下啟用)
  • 配置應用程序池的自動啟動(默認情況下啟用)
  • 啟用應用程序池的始終運行模式并配置自動啟動功能
    • 步驟二:創建類

    實現該IProcessHostPreloadClient接口的特殊類。它將在啟動期間和每個應用程序池回收后由Windows進程激活服務自動調用。

    public class ApplicationPreload : System.Web.Hosting.IProcessHostPreloadClient {public void Preload(string[] parameters){HangfireBootstrapper.Instance.Start();} }

    HangfireBootstrapper按如下方式創建類。由于這兩個Application_Start和Preload方法將在啟用了自動啟動的環境中被調用,我們需要確保初始化邏輯將被調用一次。

    public class HangfireBootstrapper : IRegisteredObject {public static readonly HangfireBootstrapper Instance = new HangfireBootstrapper();private readonly object _lockObject = new object();private bool _started;private BackgroundJobServer _backgroundJobServer;private HangfireBootstrapper(){}public void Start(){lock (_lockObject){if (_started) return;_started = true;HostingEnvironment.RegisterObject(this);GlobalConfiguration.Configuration.UseSqlServerStorage("connection string");// Specify other options here_backgroundJobServer = new BackgroundJobServer();}}public void Stop(){lock (_lockObject){if (_backgroundJobServer != null){_backgroundJobServer.Dispose();}HostingEnvironment.UnregisterObject(this);}}void IRegisteredObject.Stop(bool immediate){Stop();} }

    然后,global.asax.cs按如下所述更新您的文件。調用類實例的方法很重要,同樣重要的是在沒有啟用自動啟動功能的環境中啟動Hangfire服務器(例如,在開發機器上)。

    public class Global : HttpApplication {protected void Application_Start(object sender, EventArgs e){HangfireBootstrapper.Instance.Start();}protected void Application_End(object sender, EventArgs e){HangfireBootstrapper.Instance.Stop();} }
    • 步驟三:啟用服務自動啟動
      創建上面的類后,您應該編輯全局applicationHost.config文件(%WINDIR%\System32\inetsrv\config\applicationHost.config)。首先,您需要將應用程序池的啟動模式更改為AlwaysRunning,然后啟用Service AutoStart Providers。
      進行這些更改后,將自動重新啟動相應的應用程序池。確保僅在修改所有元素后保存更改
    <applicationPools><add name="MyAppWorkerProcess" managedRuntimeVersion="v4.0" startMode="AlwaysRunning" /> </applicationPools><!-- ... --><sites><site name="MySite" id="1"><application path="/" serviceAutoStartEnabled="true"serviceAutoStartProvider="ApplicationPreload" /></site> </sites><!-- Just AFTER closing the `sites` element AND AFTER `webLimits` tag --> <serviceAutoStartProviders><add name="ApplicationPreload" type="WebApplication1.ApplicationPreload, WebApplication1" /> </serviceAutoStartProviders>

    請注意,對于最后一個條目,WebApplication1.ApplicationPreload是應用程序中實現的類的全名,IProcessHostPreloadClient并且WebApplication1是應用程序庫的名稱。你可以在這里相關信息。無需將IdleTimeout設置為零 - 當應用程序池的啟動模式設置為時AlwaysRunning,空閑超時不再起作用。

    =!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!=
    官方文檔中的applicationHost文件中配置serviceAutoStartProviders的介紹有坑!

    <add name="ApplicationPreload" type="WebApplication1.ApplicationPreload, WebApplication1" />

    實際應為:

    <add name="ApplicationPreload" type="MyNamespace.ApplicationPreload, MyLibrary" />

    其實此處不過是對于IIS網站自動啟動的一種應用:
    其原理為 [9] :當冷啟動IIS 7.5服務器或回收單個應用程序池時,IIS 7.5使用applicationHost.config文件中的信息來確定需要自動啟動哪些Web應用程序。對于標記為自動啟動的每個應用程序,IIS7.5向ASP.NET 4發送請求,以在應用程序暫時不接受HTTP請求的狀態下啟動應用程序。當它處于此狀態時,ASP.NET將實例化serviceAutoStartProvider屬性定義的類型(如上例所示)并調用其公共入口點。
    初始化代碼在Preload方法中運行并且方法返回后,ASP.NET應用程序已準備好處理請求。
    通過在IIS .5和ASP.NET 4中添加自動啟動功能,您現在可以在處理第一個HTTP請求之前執行昂貴的應用程序初始化。例如,您可以使用新的自動啟動功能初始化應用程序,然后向負載均衡器發出信號,表明應用程序已初始化并準備接受HTTP流量。

    再一個問題,就是如果此處配置了這個,同時startup中也配置了實例化服務,那么你會發現這樣一種情況,就是會出現兩個serve同時存在的情況。因為Startup類和ApplicationPreload 類均被調用了一遍,里面各實例化了一個服務端。是這樣的,Hangfire官網有一句話:By default, Hangfire Server instance in a web application will not be started until the first user hits your site. 也就是說,默認情況下,無人訪問時,即便網站正在運行未回收,Hangfire是不會啟動的(Startup)。然而ApplicationPreload會在每次網站準備回收準備實現always running時都會被調用一次。所以碰到這種情況,如果擔心機器配置或數據庫連接池不足以支撐兩個服務端的最大工作數量的話,可以將startup中的服務實例化去掉。其余暫時尚未發現問題。
    =!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!=

    • 確保自動啟動功能正常工作

    最簡單的方法 - 回收您的應用程序池,等待5分鐘,然后轉到Hangfire Dashboard UI并檢查當前的Hangfire Server實例是否在5分鐘前啟動。

    • 如果出了什么問題

    如果在進行這些更改后您的應用程序無法加載,請打開控制面板→管理工具→事件查看器,檢查Windows事件日志。然后打開Windows日志→應用程序并查找最近的錯誤記錄.

    • Azure Web應用程序
      為Microsoft Azure中托管的應用程序啟用始終運行功能更簡單:只需打開“配置”頁面上的開關并保存設置即可。Always On
      This setting does not work for free sites

      官網文檔:Making ASP.NET application always running

    開發過程需要注意的點

    • 要保證壓入隊列的方法是冪等的。因為方法可以被手動重試,也會在失敗時自動重試。雖然可以通過[Retry(0)]過濾器應用于精確方法或全局來禁用自動重試,但作為一般規則請記住,你的job會至少執行一次。[8]
    • 關于隊列之間的執行優先級順序,優先級是基于字母順序的。是的,使用hangfire時,優先級是基于字母順序的,因為在table variable or temporary table中更復雜的查詢會降低吞吐量。所以如果想要隊列之間有優先級順序就按照首字母排序吧。官方文檔對隊列這塊只說了一句順序是很重要的,以至于誤導了一部分人一開始就認為是按照索引順序。該點獲取來源

    體驗得到的一些缺點/注意點

    • 多個服務器時,服務器與服務器之間是互不關心的。你即無法指定由哪個服務器去執行任務,也無法設置服務器優先級。服務器之間的負載均衡等就由hangfire自己調控。
    • 任務執行當出現分鐘級別的時間差時,要多注意了。盡管hangfire能夠精確到分級別,但是根據論壇討論和反饋,在分鐘級別的時間差上,可能因為延遲、性能、隊列優先級的問題等,導致一些問題無法精確執行或不執行作業,如設置兩個任務均每分鐘執行一次,這種情況。
    • 當執行壓力過大,如超出數據庫連接池等情況,會出現作業堆積或隨機鎖死作業的情況。這是從論壇中獲取來的情況。
    • 當碰到異常需要重試時,“未找到目標方法”這個錯誤的重試時間間隔似乎是不固定的,第一次是5分鐘(3:55創建,4:00重試),第二次是19分鐘(4:19重試),第三次是24分鐘(4:43重試),請注意,此時該作業被放置在計劃中,被放在重試中,而非被打上‘失敗’標簽,也未放入失敗隊列中。
    • 根據多次驗證,如果不指定隊列,那么就是會使用default隊列。所以不指定隊列和指定隊列‘default’是一樣的。它會在有default的服務器中隨機一個服務器去執行(下一篇細說,同時一種偏門解決本地調試不要與其它服務端混淆的方法)。

    留疑/思索

  • 若一個作業正在進行中,此時hangfire宿主服務被關閉,該作業會被如何處理?hangfire再次開啟時又會如何?中間若產生了不完全數據會被如何處理?
  • 一個作業需要執行時間過長(循環作業之類的)尚未結束,出現了新的作業(可能會導致重復數據)的情況,該如何處理?【思路1.在執行的任務方法前加上mutex特性即可,如果作業未完成,新作業開啟的話,新作業會放入計劃中的作業隊列中,直到前面的作業完成。思路2.在作業上設置分布式鎖,直到它正確完成為止。思路可用么?】

  • 感謝以下參考資料:
    [1] 云棲社區》HangFire分布式后端作業調度框架服務
    [2] Hangfire項目實踐分享
    [3] 定時任務組件Hangfire解析
    [4] 為DashBoard頁面添加權限認證
    [5] C#中Byte轉換相關的函數
    [6] wall-ee》hangfire使用筆記
    [7] 簡書》.NET Core開源組件:后臺任務利器之Hangfire
    [8]Are your methods ready to run in background?(官方文檔中的連接)
    [9]ASP.NET4和Visual Studio2010 Web開發概述》自動啟動Web應用程序

    總結

    以上是生活随笔為你收集整理的C#-初识Hangfire的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。