文檔目錄html
本節內容:web
ABP提供了後臺做業和工做者,用來在後臺線程裏執行應用裏的某些任務。windows
後臺做業用一種隊列且持久穩固的方式安排一些待執行後臺任務,你可能有幾個理由,須要用到後臺做業,例如:ide
查看後臺做業存儲小節獲取有關做業持久化的更多信息。.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的信息:
默認的BackgroundJobManager須要一個數據存儲來保存和獲取做業,若是你沒有實現IBackgroundJobStore,它會使用InMemoryBackgroundJobStore,它不在持久化的數據庫中保存做業,你能夠簡單的實現這個接口,讓做業存儲到一個數據庫或使用已經實現該接口的module-zero。
若是你使用第三方的做業管理器(如Hangfire),不須要實現IBackgroundJobStore。
你能夠在模塊的PreInitialize方法裏,使用Configuration.BackgroundJobs配置你的後臺做業系統。
你可能會想爲你的應用禁用後臺做業執行:
public class MyProjectWebModule : AbpModule { public override void PreInitialize() { Configuration.BackgroundJobs.IsJobExecutionEnabled = false; } //... }
不多須要這樣,但考慮一下你正在運行一個應用的多個實例並訪問同一個數據庫,這種狀況下,每一個應用將向同個數據庫查詢做業並執行它們,這可能致使同個做業的屢次執行和一些其它問題,爲阻止這種狀況,咱們有兩個選擇:
後臺做業管理器設計成可被其它後臺管理器所替換,查看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裏。
在完成建立後臺工做者後,須要把它添加到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