ABP文檔 - 後臺做業和工做者

文檔目錄html

 

本節內容:web

 

簡介數據庫

ABP提供了後臺做業和工做者,用來在後臺線程裏執行應用裏的某些任務。windows

 

後臺做業async

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

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

關於做業持久化spa

查看後臺做業存儲小節獲取有關做業持久化的更多信息。.net

 

建立一個後臺做業線程

咱們能夠經過繼承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

相關文章
相關標籤/搜索