程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> <<ABP文檔>> 後台作業和工作者,abp後台

<<ABP文檔>> 後台作業和工作者,abp後台

編輯:關於.NET

<<ABP文檔>> 後台作業和工作者,abp後台


文檔目錄

 

本節內容:

  • 簡介
  • 後台作業
    • 關於作業持久化
    • 創建一個後台作業
    • 在隊列裡添加一個新作業
    • 默認的後台作業管理器
      • 後台作業存儲
    • 配置
      • 禁用作業執行
    • Hangfire 集成
  • 後台工作者
    • 創建一個後台工作者
    • 注冊後台工作者
    • 後台工作者生存方式
  • 使你的應用一直運行

 

簡介

ABP提供了後台作業和工作者,用來在後台線程裡執行應用裡的某些任務。

 

後台作業

後台作業用一種隊列且持久穩固的方式安排一些待執行後台任務,你可能有幾個理由,需要用到後台作業,例如:

  • 為執行長時間運行的任務而用戶無需等待,例如:用戶按了一下“報告”按鈕開始一個長時間運行的報告任務,你把這個任務添加到隊列裡,它任務完成後,通過電子郵件發送報告結果給你。
  • 為創建可重試且持久穩固的任務來保證一個代碼將會被完成運行,例如:你可以在後台作業裡發送一個電子郵件,為克服臨時的失敗且保證它最終將會被發送,當然當發送電子郵件時用戶也無需等待。

關於作業持久化

查看後台作業存儲小節獲取有關作業持久化的更多信息。

 

創建一個後台作業

我們可以通過繼承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);
    }
}

我們注入user倉儲(可獲取用戶電子郵件)和郵件發送器(一個發送郵件的服務),並簡單的發送郵件,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; }
}

一個作業的參數應當可序列化,因為它要被序列化後存儲到數據庫,雖然ABP默認後台作業管理器使用JSOn序列化器(它不需要使用[Serializable]特性),更好還是定義為[Serializable],因為將來我們可能會替換成另一個作業管理器,可能會使用.net內置的二進制序列化器。

保存你的參數簡單(如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);
    }
}

當加入隊列時我們發送參數42, IBackgroundManager將實體化並以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
            });
    }
}

Enqueue(或EnqueueAsync) 有其它參數,如priority和delay。

 

默認的後台作業管理器

BackgroundJobManager默認實現了IBackgroundJobManager,它可被其它後台作業提供器替代(查看Hangfire文檔)。如下為一些關於默認BackgroudJobManager的信息:

  • 它是一個簡單的作業隊列,以FIFO(先進先出)方式單線程作業,它使用IBackgroundJobStore來持久化作業(見下一小節)。
  • 它一直重試作業執行直到作業成功運行(只記錄日志不拋出異常)或超時,默認超時為一個作業2天。
  • 在作業成功運行後,它從存儲(數據庫)裡刪除這個作業,如果超時了,就把這個作業設置為“被拋棄的”,然後離開數據庫。
  • 它在重試一個作業之間遞增等待時間,第一次重試,等待1分鐘,第二次重試,等待2分鐘,第三次重試,等待4分鐘,如此類推。
  • 它在固定的間隔裡給作業的存儲投票,查詢作業按優先級(升序)排序,然後按嘗試次數(程序)排。

 

後台作業存儲

默認的BackgroundJobManager需要一個數據存儲來保存和獲取作業,如果你沒有實現IBackgroundJobStore,它會使用InMemoryBackgroundJobStore,它不在持久化的數據庫中保存作業,你可以簡單的實現這個接口,讓作業存儲到一個數據庫或使用已經實現該接口的module-zero。

如果你使用第三方的作業管理器(如Hangfire),不需要實現IBackgroundJobStore。

 

配置

你可以在模塊的PreInitialize方法裡,使用Configuration.BackgroundJobs配置你的後台作業系統。

 

禁用作業執行

你可能會想為你的應用禁用後台作業執行:

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

    //...
}

很少需要這樣,但考慮一下你正在運行一個應用的多個實例並訪問同一個數據庫,這種情況下,每個應用將向同個數據庫查詢作業並執行它們,這可能導致同個作業的多次執行和一些其它問題,為阻止這種情況,我們有兩個選擇:

  • 你可以只允許應用的一個實例來完成作業的執行。
  • 你可以禁用應用的所有實例執行作業,再單獨創建一個應用(如:一個windows服務)來執行後台作業。

 

Hangfire 集成 

後台作業管理器設計成可被其它後台管理器所替換,查看Hangfire集成文檔如何用Hangfire代替。 

 

後台工作者

後台工作者與後台作業不同,它簡單的依賴應用在後台運行的線程,通常地,它定義執行一些任務,例如:

  • 一個後台工作者可以定期刪除舊日志。
  • 一個後台工作者可以定期檢測不活躍的用戶,然後發郵件給他們,讓他們重新使用你的應用。

 

創建一個後台工作者

為創建一個後台工作者,我們應當實現IBackgroundWorker接口,我們還可以選擇直接從BackgroundWorkerBase或PeriodicBackgroundWorkerBase基類上繼承。

假設我們想把超過30天未登錄的用戶設置為“消極”的,代碼如下:

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();
        }
    }
}

 

這是一段真實的代碼,它工作在ABP的module-zero裡。

  • 如果你從PeriodicBackgroundWorkerBase繼承(如這個例子),需要實現DoWork方法來執行你的定期工作。
  • 如果你從BackgroundWorkerBase繼承或直接實現IBackgroundWorker,需要重寫/實現Start、Stop和WaitToStop方法,Start和Stop方法應當是非阻塞的,WaitToStop方法需要等待工作者完成它當前的工作。

 

注冊後台工作者

在完成創建後台工作者後,需要把它添加到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工作者實例用來監視和刪除不同的日志目錄)。

 

使你的應用一直運行

只有當你的應用運行時,後台作業和工作者才能工作,如果一個Web應用長時間沒有收到訪問請求,它默認地會被關閉,所以,如果你的宿主後台作業運行在你的web應用裡(這是默認行為),你應當確保你的web應用被配置成一直運行,否則,只有當你的應用在使用的時候,後台作業才能工作。

這裡有些技術可以做到這點,一個非常簡單的辦法是:從一個外部應用裡定期訪問你的Web應用,從而你可以一直檢查你的web應用是否一直運行著。Hangfire文檔解釋了一些其它方法。

 

kid1412附:英文原文:http://www.aspnetboilerplate.com/Pages/Documents/Background-Jobs-And-Workers

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved