ABP module-zero +AdminLTE+Bootstrap Table+jQuery權限管理系統第十四節--後臺工做者HangFire與ABP框架Abp.Hangfire及擴展

返回總目錄:ABP+AdminLTE+Bootstrap Table權限管理系統一期
HangFire與Quartz.NET相比主要是HangFire的內置提供集成化的控制檯,方便後臺查看及監控,對於你們來講,比較方便。html

HangFire是什麼

Hangfire是一個開源框架(.NET任務調度框架),能夠幫助您建立,處理和管理您的後臺做業,處理你不但願放入請求處理管道的操做:git

  • 通知/通信;
  • xml,csv,json批量導入;
  • 建立檔案;
  • 發射web hooks;
  • 刪除用戶;
  • 創建不一樣的圖表;
  • 圖像/視頻處理;
  • 清除臨時文件;
  • 反覆出現的自動報告;
  • 數據庫維護

Hangfire支持全部類型的後臺任務 - 短期運行和長時間運行, CPU intensiveI/O intensive,一次性的和常常性的。你不須要從新發明輪子 ,能夠直接使用。
Hangfire包含三大核心組件:客戶端、持久化存儲、服務端。看看官方的這張圖:github

image.png

Hangfire基礎

  • 基於隊列的任務處理(Fire-and-forget)
    延遲做業也只執行一次,但不會當即執行 - 只能在指定的時間間隔後執行。
var jobId = BackgroundJob.Schedule(
    () => Console.WriteLine("Delayed!"),
    TimeSpan.FromDays(7));
  • 定時執行(Recurring)
    按照指定的CRON計劃, 重複執行的做業會被屢次觸發。
RecurringJob.AddOrUpdate(
    () => Console.WriteLine("Recurring!"),
    Cron.Daily);
  • 延續性執行(Continuations)
    延續性任務相似於.NET中的Task,能夠在第一個任務執行完以後緊接着再次執行另外的任務:
BackgroundJob.ContinueWith(
    jobId,
    () => Console.WriteLine("Continuation!"));
  • 延時執行任務(Delayed)
    延遲做業也只執行一次,但不會當即執行 - 只能在指定的時間間隔後執行。
var jobId = BackgroundJob.Schedule(
    () => Console.WriteLine("Delayed!"),
    TimeSpan.FromDays(7));
  • 批處理(Batches)
    批處理是一組自動建立的後臺做業。
var batchId = Batch.StartNew(x =>
{
    x.Enqueue(() => Console.WriteLine("Job 1"));
    x.Enqueue(() => Console.WriteLine("Job 2"));
});
  • 延時批處理(Batch Continuations)
    批處理在父類完成後觸發後臺做業。
Batch.ContinueWith(batchId, x =>
{
    x.Enqueue(() => Console.WriteLine("Last Job"));
});
  • 後臺進程(Background Process)
    當你須要在應用程序的整個生命週期中連續運行後臺進程時使用它們。
public class CleanTempDirectoryProcess : IBackgroundProcess
{
    public void Execute(BackgroundProcessContext context)
    {
        Directory.CleanUp(Directory.GetTempDirectory());
        context.Wait(TimeSpan.FromHours(1));
    }
}

後臺做業是應用程序中很是重要的部分,Hangfire確保至少執行一次任務。要在應用程序從新啓動之間保留後臺做業信息,全部信息都將保存在您最喜歡的持久性存儲中。
        Hangfire將您的任務保存到持久化庫彙總,而且以可靠的方式處理它們。這意味着,你能夠中斷Hangfire Worder的線程,從新加載應用程序域,或者終止程序,即便這樣您的任務仍會被處理。只有在你代碼的最後一行執行完成,Hangfire纔會標記這個任務完成。而且知道任務可能在最後一行代碼執行以前失敗。它包含多種 自動-重試機制,它能夠自動處理在存儲或代碼執行過程當中發生的錯誤。
       這對於通用託管環境(如IIS Server)很是重要。它們能夠包含不的
優化,超時和錯誤處理代碼 (可能致使進程終止)來防止很差的事情發生。若是您沒有使用可靠的處理和自動機制,您的工做可能會丟失。您的最終用戶可能無限期等待某些任務,如電子郵件,報告,通知等。web

實操演練

光說不練假把式,下面咱們新建一個web項目,而後NuGet引入這幾個程序集
image.png數據庫

配置

而後在App_Start文件夾下Startup類配置下。
首先指定數據庫,指定Hangfire使用內存存儲後臺任務信息.
Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("Default");
而後啓用HangfireServer這個中間件(它會自動釋放)
app.UseHangfireServer();
而後啓用Hangfire的儀表盤(能夠看到任務的狀態,進度等信息)
app.UseHangfireDashboard();
而後配置下前臺路由json

app.UseHangfireDashboard("/hangfire", new DashboardOptions
 {
         Authorization = new[] { new AbpHangfireAuthorizationFilter() }
  });

而後就是加入上面已經列出的幾個例子。服務器

var jobId = BackgroundJob.Schedule(
                () => Console.WriteLine("Delayed!"),
                TimeSpan.FromDays(7));


            RecurringJob.AddOrUpdate(
                        () => Console.WriteLine("Recurring!"),
                        Cron.Daily);

            BackgroundJob.ContinueWith(
                            jobId,
                            () => Console.WriteLine("Continuation!"));


            var jobId2 = BackgroundJob.Schedule(
                        () => Console.WriteLine("Delayed!"),
                        TimeSpan.FromDays(7));

效果

運行項目,輸入路徑http://<your-site>/hangfire而後就能夠看到界面了。
image.png併發

image.png
image.png
咱們分別點擊上面界面中的「加入隊列」「當即執行按鈕」,就獲得下面這幅圖片。
image.png
點擊進去,能夠看到以下圖。
image.png
image.pngapp

界面看起來很清爽,並且一目瞭然。這就是可視化界面的好處。框架

Abp.Hangfire

ASP.NET Boilerplate提供後臺做業和後臺工做者,用於在應用程序的後臺線程中執行某些任務。
後臺做業用於排隊某些任務,以隊列和持續的方式在後臺執行。
咱們能夠經過從BackgroundJob <TArgs>類繼承或直接實現IBackgroundJob <TArgs>接口來建立後臺做業類。
這是最簡單的後臺工做:

public class TestJob : BackgroundJob<int>, ITransientDependency
{
    public override void Execute(int number)
    {
        Logger.Debug(number.ToString());
    }
}

後臺做業定義了一個Execute方法獲取輸入參數。參數類型被定義爲泛型 類參數,如示例中所示。後臺工做必須註冊到依賴注入系統中,實現ITransientDependency是最簡單的方式。下面定義一個更實際的工做,在後臺隊列中發送電子郵件:

public class SimpleSendEmailJob : BackgroundJob<SimpleSendEmailJobArgs>, ITransientDependency
{
    private readonly IRepository<User, long> _userRepository;
    private readonly IEmailSender _emailSender;

    public SimpleSendEmailJob(IRepository<User, long> userRepository, IEmailSender emailSender)
    {
        _userRepository = userRepository;
        _emailSender = emailSender;
    }

    public override void Execute(SimpleSendEmailJobArgs args)
    {
        var senderUser = _userRepository.Get(args.SenderUserId);
        var targetUser = _userRepository.Get(args.TargetUserId);

        _emailSender.Send(senderUser.EmailAddress, targetUser.EmailAddress, args.Subject, args.Body);
    }
}

咱們注入了用戶倉儲(爲了得到用戶信息)和email發送者(發送郵件的服務),而後簡單地發送了該郵件。SimpleSendEmailJobArgs是該工做的參數,它定義以下:

[Serializable]
public class SimpleSendEmailJobArgs
{
    public long SenderUserId { get; set; }

    public long TargetUserId { get; set; }

    public string Subject { get; set; }

    public string Body { get; set; }
}

做業參數應該是可序列化的,由於它 被序列化並存儲在數據庫中。雖然ASP.NET Boilerplate默認後臺做業管理器使用JSON 序列化(不須要[Serializable]屬性),但最好定義[Serializable]屬性,由於未來可能會切換到另外一個做業管理器,在二進制序列化。保持你的參數簡單(如 DTO),不要包含 實體或其餘不可序列化的對象。如SimpleSendEmailJob示例所示,咱們只能存儲 一個實體的Id,並從做業中的存儲庫獲取該實體。將新做業添加到隊列中定義後臺做業後,咱們能夠注入並使用IBackgroundJobManager 將做業添加到隊列中。查看上面定義的TestJob示例:

public class MyService
{
    private readonly IBackgroundJobManager _backgroundJobManager;

    public MyService(IBackgroundJobManager backgroundJobManager)
    {
        _backgroundJobManager = backgroundJobManager;
    }

    public void Test()
    {
        _backgroundJobManager.Enqueue<TestJob, int>(42);
    }
}

當入隊(Enqueue)時,咱們將42做爲參數傳遞。IBackgroundJobManager將會實例化並使用42做爲參數執行TestJob
讓咱們看一下如何爲上面定義的SimpleSendEmailJob添加一個新的工做:

[AbpAuthorize]
public class MyEmailAppService : ApplicationService, IMyEmailAppService
{
    private readonly IBackgroundJobManager _backgroundJobManager;

    public MyEmailAppService(IBackgroundJobManager backgroundJobManager)
    {
        _backgroundJobManager = backgroundJobManager;
    }

    public async Task SendEmail(SendEmailInput input)
    {
            await _backgroundJobManager.EnqueueAsync<SimpleSendEmailJob, SimpleSendEmailJobArgs>(
            new SimpleSendEmailJobArgs
            {
                Subject = input.Subject,
                Body = input.Body,
                SenderUserId = AbpSession.GetUserId(),
                TargetUserId = input.TargetUserId
            });
    }
}

Enqueu(或EnqueueAsync)方法具備其餘參數,如優先級 和延遲。

默認後臺做業管理器

IBackgroundJobManagerBackgroundJobManager默認實現。它能夠被另外一個後臺做業提供者替代(參見 hangfire集成)。有關默認BackgroundJobManager的一些信息:
這是一個簡單的做業隊列在 單線程中做爲FIFO使用。它使用IBackgroundJobStore來堅持做業。

  • 它重試做業執行,直到做業 成功運行(不會拋出任何異常,但記錄它們)或 超時。做業的默認超時時間爲2天。
  • 它成功執行時從商店(數據庫)中刪除做業。若是超時,則將其設置爲廢棄並保留在數據庫中。
  • 它愈來愈多地等待從新工做。等待1分鐘第一次重試,2分鐘第二次重試,4分鐘第三次重試等等。
  • 它以固定的時間間隔輪詢商店的工做。按優先級(asc)查詢做業,而後按try count(asc)進行排序。

後臺工做存儲

默認的BackgroundJobManager須要一個數據存儲來保存和獲取做業。若是您沒有實現IBackgroundJobStore,那麼它使用 InMemoryBackgroundJobStore,它不會將做業保存在持久數據庫中。您能夠簡單地實現它來將做業存儲在數據庫中,或者可使用 已經實現它的module-zero
若是您使用第三方工做經理(如 Hanfgire),則無需實施IBackgroundJobStore

配置

您能夠在 模塊的PreInitialize方法中使用Configuration.BackgroundJobs來配置後臺做業系統。
禁用做業執行
您可能須要爲應用程序禁用後臺做業執行:

public class MyProjectWebModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
    }

    //...
}

這種狀況不多見。可是,認爲您正在同一個數據庫上運行應用程序的多個實例(在Web場中)。在這種狀況下,每一個應用程序將查詢做業的相同數據庫並執行它們。這致使同一個工做的多個執行和一些其餘問題。爲了防止它,你有兩個選擇:

  • 您只能爲應用程序的一個實例啓用做業執行。
  • 您能夠禁用全部Web應用程序實例的做業執行,並建立一個執行後臺做業的獨立應用程序(例如:Windows服務)。

異常處理

因爲默認的後臺做業管理器應該從新嘗試失敗的做業,它會處理(並記錄)全部異常。若是你想在發生異常時獲得通知,你能夠建立一個事件處理程序來處理AbpHandledExceptionData。後臺管理器用一個包裝了真正異常的BackgroundJobException異常對象觸發這個事件(對於實際的異常,獲得InnerException)。

Hangfire集成

  • 後臺做業管理器被設計爲可被另外一個後臺做業管理器替換。請參閱 Hangfire集成文檔以用Hangfire替換它。
  • 後臺工做者與後臺工做不一樣。它們是在後臺運行的應用程序中的簡單 獨立線程。一般,他們按期執行一些任務。例子;
  • 後臺工做人員能夠按期運行以 刪除舊日誌。
  • 後臺工做人員能夠按期來 判斷非活躍用戶和發送電子郵件要返回給應用程序。

建立一個後臺工做者

要建立一個後臺工做者,咱們應該實現 IBackgroundWorker接口。或者,咱們能夠根據咱們的須要從BackgroundWorkerBasePeriodicBackgroundWorkerBase繼承 。
假設咱們想在最近30天內沒有登陸到應用程序,使用戶狀態passive。看代碼:

public class MakeInactiveUsersPassiveWorker : PeriodicBackgroundWorkerBase, ISingletonDependency
{
    private readonly IRepository<User, long> _userRepository;

    public MakeInactiveUsersPassiveWorker(AbpTimer timer, IRepository<User, long> userRepository)
        : base(timer)
    {
        _userRepository = userRepository;
        Timer.Period = 5000; //5 seconds (good for tests, but normally will be more)
    }

    [UnitOfWork]
    protected override void DoWork()
    {
        using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.MayHaveTenant))
        {
            var oneMonthAgo = Clock.Now.Subtract(TimeSpan.FromDays(30));

            var inactiveUsers = _userRepository.GetAllList(u =>
                u.IsActive &&
                ((u.LastLoginTime < oneMonthAgo && u.LastLoginTime != null) || (u.CreationTime < oneMonthAgo && u.LastLoginTime == null))
                );

            foreach (var inactiveUser in inactiveUsers)
            {
                inactiveUser.IsActive = false;
                Logger.Info(inactiveUser + " made passive since he/she did not login in last 30 days.");
            }

            CurrentUnitOfWork.SaveChanges();
        }
    }
}

這是現實的代碼,能夠直接在module-zero的 ASP.NET Boilerplate中運行 。

  • 若是您從PeriodicBackgroundWorkerBase派生(如本示例中所示),則應該實施DoWork方法來執行您的按期工做代碼。
  • 若是從BackgroundWorkerBase派生或直接實現IBackgroundWorker,則將覆蓋/實現StartStopWaitToStop方法。StartStop方法應該是非阻塞的,WaitToStop方法應該等待 worker完成當前的關鍵任務。

註冊後臺工做者

建立後臺工做者後,咱們應該將其添加到 IBackgroundWorkerManager。最多見的地方是你的模塊的PostInitialize方法:

public class MyProjectWebModule : AbpModule
{
    //...

    public override void PostInitialize()
    {
        var workManager = IocManager.Resolve<IBackgroundWorkerManager>();
        workManager.Add(IocManager.Resolve<MakeInactiveUsersPassiveWorker>());
    }
}

雖然咱們一般在PostInitialize中加入工做人員,但對此沒有限制。您能夠在任何地方注入IBackgroundWorkerManager,並在運行時添加工做人員。當您的應用程序正在關閉時,IBackgroundWorkerManager將中止並釋放全部註冊的工做人員。

後臺工做者生命週期

後臺工做人員一般以單例的。可是沒有限制。若是您須要同一工人類的多個實例,則能夠將其設置爲暫時的,並向IBackgroundWorkerManager添加多個實例。在這種狀況下,您的工做人員多是參數化的(例如,您有一個LogCleaner類,可是他們監視的兩個LogCleaner工做者實例並清除不一樣的日誌文件夾)。

高級調度

ASP.NET Boilerplate的後臺工做系統很簡單。除了按期運行的工人以外,它沒有一個時間表系統。若是您須要更高級的計劃功能,咱們建議您檢查Quartz或其餘庫。

讓您的應用程序一直運行

後臺做業和工做人員只有在您的應用程序正在運行時纔有效 若是很長一段時間沒有對Web應用程序執行任何請求,ASP.NET應用程序將默認關閉。所以,若是您在Web應用程序中託管後臺做業(這是默認行爲),則應確保您的Web應用程序配置爲始終運行。不然,後臺做業只在您的應用程序正在使用時才起做用。
有一些技術來完成這一點。最簡單的方法是按期從外部應用程序請求您的Web應用程序。所以,您也能夠檢查您的Web應用程序是否已啓動並正在運行。 Hangfire文檔解釋了其餘一些方法。
經過以上官方文檔,咱們在程序裏配置一下。
image.png

運行一下效果是同樣的。
image.png
image.png

Hangfire優勢

Hangfire是一個後臺可監控的應用,不用每次都要從服務器拉取日誌查看,在沒有ELK的時候至關不方便。Hangfire控制面板不只提供監控,也能夠手動的觸發執行定時任務。若是在定時任務處理方面沒有很高的要求,好比必定要5s定時執行,Hangfire值得擁有。拋開這些,Hangfire優點太明顯了:

  • 持久化保存任務、隊列、統計信息
  • 重試機制
  • 多語言支持
  • 支持任務取消
  • 支持按指定Job Queue處理任務
  • 服務器端工做線程可控,即job執行併發數控制
  • 分佈式部署,支持高可用
  • 良好的擴展性,如支持IOCHangfire Dashboard受權控制、Asp.net Core、持久化存儲等

Hangfire擴展

Hangfire擴展性你們能夠參考這裏,有幾個擴展是很實用的.下面這些關於Hangfire擴展你們能夠本身查資料。後面若是有機會的話,我再補上。

  • Hangfire Dashborad日誌查看
  • Hangfire Dashborad受權
  • IOC容器之Autofac
  • RecurringJob擴展
  • 與MSMQ集成
  • 持久化存儲之Redis

福利及其餘

其實Hangfire仍是蠻簡單的。若是你須要瞭解更多關於Abp.Hangfire的內容,建議你去看一下github上一個專門關於Abp.Hangfire的demo,
地址:https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/BackgroundJobAndNotificationsDemo
另外 ABP後臺工做者類使用HANGFIRE這篇文章
講解abp Hangfire 缺點是工做者類依賴了具體的基類(PeriodicBackgroundWorkerBase),就會存在應用程序耦合。以及解決耦合的辦法,算是對abp Hangfire的擴展,我不太認同,各有見解吧。

就以上面我說到的項目 abp Hangfire demo 項目 BackgroundJobAndNotificationsDemo爲例,首先是一個建立郵件發送的任務。
image.png
image.png
,建立到數據庫以後,HangFire會自動在數據庫建立幾張表。
image.png
而後配置我上面說到的幾個配置步驟以後。運行項目能夠看到。每隔5秒鐘會請求一下後臺任務。
image.png
界面上就有了相應的效果

image.png
你們能夠自行下載Demo下來看一下相關的寫法以及配置。

本文githubd地址:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS.git
後臺工做者HangFire與ABP框架Abp.Hangfire及擴展
返回總目錄:ABP+AdminLTE+Bootstrap Table權限管理系統一期

相關文章
相關標籤/搜索